UncountablePythonSDK 0.0.73__py3-none-any.whl → 0.0.75__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.
- {UncountablePythonSDK-0.0.73.dist-info → UncountablePythonSDK-0.0.75.dist-info}/METADATA +1 -1
- {UncountablePythonSDK-0.0.73.dist-info → UncountablePythonSDK-0.0.75.dist-info}/RECORD +20 -20
- {UncountablePythonSDK-0.0.73.dist-info → UncountablePythonSDK-0.0.75.dist-info}/WHEEL +1 -1
- pkgs/argument_parser/__init__.py +1 -0
- pkgs/argument_parser/argument_parser.py +13 -1
- pkgs/serialization/serial_class.py +13 -0
- pkgs/type_spec/builder.py +68 -21
- pkgs/type_spec/emit_python.py +0 -3
- pkgs/type_spec/emit_typescript.py +18 -239
- pkgs/type_spec/emit_typescript_util.py +235 -2
- pkgs/type_spec/type_info/emit_type_info.py +7 -7
- pkgs/type_spec/util.py +1 -1
- uncountable/integration/queue_runner/worker.py +14 -14
- uncountable/types/api/entity/create_entities.py +1 -1
- uncountable/types/api/entity/create_entity.py +1 -1
- uncountable/types/api/uploader/invoke_uploader.py +2 -1
- uncountable/types/async_batch_processor.py +4 -1
- uncountable/types/client_base.py +6 -3
- uncountable/types/entity_t.py +2 -0
- {UncountablePythonSDK-0.0.73.dist-info → UncountablePythonSDK-0.0.75.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.
|
|
3
|
+
Version: 0.0.75
|
|
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
|
|
@@ -26,10 +26,10 @@ examples/integration-server/jobs/materials_auto/example_cron.py,sha256=7VVQ-UJsq
|
|
|
26
26
|
examples/integration-server/jobs/materials_auto/profile.yaml,sha256=MiqT9AHWoFz-rcpAHfiFTXuCP-18DaFipUaceati0-0,365
|
|
27
27
|
pkgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
pkgs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
-
pkgs/argument_parser/__init__.py,sha256=
|
|
29
|
+
pkgs/argument_parser/__init__.py,sha256=JRfZkC0-q6axr8F5_TKrjSprJ7d7chfcPvf-iMQqFg0,447
|
|
30
30
|
pkgs/argument_parser/_is_enum.py,sha256=Gw6jJa8nBwYGqXwwCZbSnWL8Rvr5alkg5lSVAqXtOZM,257
|
|
31
31
|
pkgs/argument_parser/_is_namedtuple.py,sha256=Rjc1bKanIPPogl3qG5JPBxglG1TqWYOo1nxxhBASQWY,265
|
|
32
|
-
pkgs/argument_parser/argument_parser.py,sha256=
|
|
32
|
+
pkgs/argument_parser/argument_parser.py,sha256=Z8lR_2L_N0zuxdOsK91D_9mjlsCcR_FJwtCLIfJz5oI,17713
|
|
33
33
|
pkgs/argument_parser/case_convert.py,sha256=NuJLJUJRbyVb6_Slen4uqaStEHbcOS1d-hBBfDrrw-c,605
|
|
34
34
|
pkgs/filesystem_utils/__init__.py,sha256=NSsQrUCoGISBCqCCyq6_583sYHTVEQeDjDO8hvZn3ag,1261
|
|
35
35
|
pkgs/filesystem_utils/_gdrive_session.py,sha256=GJuZYJq1W4QQ_7OLvZIMK99FgRq8FxJHg6cMUx9prtA,11077
|
|
@@ -41,7 +41,7 @@ pkgs/filesystem_utils/filesystem_session.py,sha256=BQ2Go8Mu9-GcnaWh2Pm4x7ugLVsre
|
|
|
41
41
|
pkgs/serialization/__init__.py,sha256=LifasRW0a50A3qRFmo2bf3FQ6TXhZWOTz2-CVTgPjcQ,753
|
|
42
42
|
pkgs/serialization/missing_sentry.py,sha256=aM_9KxbCk9dVvXvcOKgkIQBqFWvLhv8QlIUCiuFEXMo,806
|
|
43
43
|
pkgs/serialization/opaque_key.py,sha256=FIfXEE0DA1U8R_taFbQ1RCoTSgehrPjP06-qvo-GeNQ,177
|
|
44
|
-
pkgs/serialization/serial_class.py,sha256=
|
|
44
|
+
pkgs/serialization/serial_class.py,sha256=D7vSnfJw4rWEDFbDd07pxgzyfTFZT5SKeQEv4C1c4H0,6057
|
|
45
45
|
pkgs/serialization/serial_union.py,sha256=xpdeqCrRd0sNCaUwBQRzje6V40ndCbJpZrLX2K0d5xo,2741
|
|
46
46
|
pkgs/serialization/yaml.py,sha256=yoJtu7_ixnJV6uTxA_U1PpK5F_ixT08AKVh5ocyYwXM,1466
|
|
47
47
|
pkgs/serialization_util/__init__.py,sha256=MVKqHTUl2YnWZAFG9xCxu1SgmkQ5xPofrAGlYg6h7rI,330
|
|
@@ -52,25 +52,25 @@ pkgs/strenum_compat/__init__.py,sha256=wXRFeNvBm8RU6dy1PFJ5sRLgUIEeH_DVR95Sv5qpG
|
|
|
52
52
|
pkgs/strenum_compat/strenum_compat.py,sha256=uOUAgpYTjHs1MX8dG81jRlyTkt3KNbkV_25zp7xTX2s,36
|
|
53
53
|
pkgs/type_spec/__init__.py,sha256=h5DmJTca4QVV10sZR1x0-MlkZfuGYDfapR3zHvXfzto,19
|
|
54
54
|
pkgs/type_spec/__main__.py,sha256=5bJaX9Y_-FavP0qwzhk-z-V97UY7uaezJTa1zhO_HHQ,1048
|
|
55
|
-
pkgs/type_spec/builder.py,sha256=
|
|
55
|
+
pkgs/type_spec/builder.py,sha256=nuHdVNOmwbHbHV5_8nqtsrxOfzIII6jcQO7eLOljT4c,48856
|
|
56
56
|
pkgs/type_spec/config.py,sha256=ZUmPWCzTwjesAqlqeL1_E_yoIUZE_8g0kI2yXtbU0Zc,4811
|
|
57
57
|
pkgs/type_spec/emit_io_ts.py,sha256=U03sQBpgRqYOaMKrPCRnYb70YboiCgaZfseCXSzW5NY,5707
|
|
58
58
|
pkgs/type_spec/emit_open_api.py,sha256=5a0iAHBbgFD4wfKuyjPvxCYYHNTjKxEHA0aYjMGSqe4,24596
|
|
59
59
|
pkgs/type_spec/emit_open_api_util.py,sha256=x4GCiZSGdypJ9Qtm6I5W_3UvwdJyMs8_OGhJ8_THznA,2401
|
|
60
|
-
pkgs/type_spec/emit_python.py,sha256=
|
|
61
|
-
pkgs/type_spec/emit_typescript.py,sha256=
|
|
62
|
-
pkgs/type_spec/emit_typescript_util.py,sha256=
|
|
60
|
+
pkgs/type_spec/emit_python.py,sha256=eBct7PMYgcv35POo2JU089lLggPrgLfTOrKpqAukn1E,47370
|
|
61
|
+
pkgs/type_spec/emit_typescript.py,sha256=PL1h2UvTp1PRMkTXH893ZYS3cPUrrupjnLg_9ndZGDQ,8838
|
|
62
|
+
pkgs/type_spec/emit_typescript_util.py,sha256=e2rGSs9OTD-iXwcHfU4V9E35jwMc5qVshhOKMknGrJ8,10319
|
|
63
63
|
pkgs/type_spec/load_types.py,sha256=vO8VLI7aTKzzHQIla-WO-5Z_mfTuwUqH4ZSKN9E9n5U,3688
|
|
64
64
|
pkgs/type_spec/open_api_util.py,sha256=IGh-_snGPST_P_8FdYtO8MTEa9PUxRW6Rzg9X9EgQik,7114
|
|
65
65
|
pkgs/type_spec/test.py,sha256=4ueujBq-pEgnX3Z69HyPmD-bullFXmpixcpVzfOkhP4,489
|
|
66
|
-
pkgs/type_spec/util.py,sha256=
|
|
66
|
+
pkgs/type_spec/util.py,sha256=79SLJsSPVnBe2_3CTF6J-7-QD9nRr6o8MKvfjyx53eI,4864
|
|
67
67
|
pkgs/type_spec/actions_registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
68
|
pkgs/type_spec/actions_registry/__main__.py,sha256=JGwKxcAmrQdbpVR2vwknoimN1Q-r5h4SADw1cYLYzgk,4331
|
|
69
69
|
pkgs/type_spec/actions_registry/emit_typescript.py,sha256=Z1ZM4zOw26tvLspvW6Emg79-jxjhNBse-8yaionbmeo,6066
|
|
70
70
|
pkgs/type_spec/parts/base.py.prepart,sha256=wGNoDyQnLolHRZGRwHQX5TrPfKnu558NXCocYvqyroc,2174
|
|
71
71
|
pkgs/type_spec/parts/base.ts.prepart,sha256=2FJJvpg2olCcavxj0nbYWdwKl6KeScour2JjSvN42l8,1001
|
|
72
72
|
pkgs/type_spec/type_info/__main__.py,sha256=pmVjVqXyVh8vKTNCTFgz80Sg74C5BKToP3E6GS-X_So,857
|
|
73
|
-
pkgs/type_spec/type_info/emit_type_info.py,sha256=
|
|
73
|
+
pkgs/type_spec/type_info/emit_type_info.py,sha256=C4Rq5z22c3IJTyQriNkQWgGV7I3ZgTDjwKm_JM_sOYI,13362
|
|
74
74
|
pkgs/type_spec/value_spec/__init__.py,sha256=Z-grlcZtxAfEXhPHsK0nD7PFLGsv4eqvunaPN7_TA84,83
|
|
75
75
|
pkgs/type_spec/value_spec/__main__.py,sha256=6bzP85p_Cm4bPp5tXz8D_4p64wMn5SKsXC7SqSZquYc,8318
|
|
76
76
|
pkgs/type_spec/value_spec/convert_type.py,sha256=Tg5YsYOwvmf_EqbCAtCmqy3-dud8OwdbEOzAaRN7cCs,2286
|
|
@@ -105,7 +105,7 @@ uncountable/integration/queue_runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
|
|
|
105
105
|
uncountable/integration/queue_runner/job_scheduler.py,sha256=n6bM6ZqVOPD0PoJuZV5Y5tuhmw2gI-_p6JbnVlK42uI,5016
|
|
106
106
|
uncountable/integration/queue_runner/queue_runner.py,sha256=0BmYu5zHdothTevGsB-nXg6MBd1UD-WkP3h1WCKMdQg,710
|
|
107
107
|
uncountable/integration/queue_runner/types.py,sha256=8qTq29BTSa5rmW6CBlBntP0pNIiDcwu1wHa78pjroS0,219
|
|
108
|
-
uncountable/integration/queue_runner/worker.py,sha256=
|
|
108
|
+
uncountable/integration/queue_runner/worker.py,sha256=Bwp7_OD4AughY01QQtZxlmZ5yrDmDk1GuijH09xhUkk,4455
|
|
109
109
|
uncountable/integration/queue_runner/command_server/__init__.py,sha256=gQPVILGpWzCr2i5GJyoqna7AOSFvtn4tav69gB78mTQ,571
|
|
110
110
|
uncountable/integration/queue_runner/command_server/command_client.py,sha256=DJb0TUVFkiiLBEQzHSN94sTRnuEbutNEgdN39XmnOXI,2046
|
|
111
111
|
uncountable/integration/queue_runner/command_server/command_server.py,sha256=yyXryhiEC2eGS0yFElLGsVzSKwOuYvj-zp22jQorkv0,2138
|
|
@@ -124,7 +124,7 @@ uncountable/integration/secret_retrieval/retrieve_secret.py,sha256=eoPWbkUtCn_63
|
|
|
124
124
|
uncountable/integration/webhook_server/entrypoint.py,sha256=hgbEtdVo3QU3odlHSygxmrsxR9g7rgU0yKt-o9nVAHE,5686
|
|
125
125
|
uncountable/types/__init__.py,sha256=KSsSEBnGhn88NPex5q9MZtY4kj8PRqVTbaKtko4hxUU,8561
|
|
126
126
|
uncountable/types/async_batch.py,sha256=_OhT25_dEVts_z_n1kqfJH3xlZg3btLqR6TNkfFLlXE,609
|
|
127
|
-
uncountable/types/async_batch_processor.py,sha256=
|
|
127
|
+
uncountable/types/async_batch_processor.py,sha256=esKBP56RiRH-VjAFBr8wgfcgKsRkUfiLBmQmYGx4XXY,11462
|
|
128
128
|
uncountable/types/async_batch_t.py,sha256=CZ-rltFUiKVowvL5BhMfWaFxgf-Z0KPsghvjsg-PweY,2493
|
|
129
129
|
uncountable/types/base.py,sha256=xVSjWvA_fUUnkCg83EjoYEFvAfmskinKFMeYFOxNc9E,359
|
|
130
130
|
uncountable/types/base_t.py,sha256=XXjZXexx0xWFUxMMhW8i9nIL6n8dsZVsHwdgnhZ0zJ4,2714
|
|
@@ -132,13 +132,13 @@ uncountable/types/calculations.py,sha256=FFO_D3BbKoGDZnqWvTKpW4KF359i2vrKjpdFCLY
|
|
|
132
132
|
uncountable/types/calculations_t.py,sha256=157qD0VqijD5kNDF5BRsfGli3WaPGnNjoo2o2CPX-Ik,669
|
|
133
133
|
uncountable/types/chemical_structure.py,sha256=E-LnikTFDoVQ1b2zKaVUIO_PAKm-7aZZYJi8I8SDSic,302
|
|
134
134
|
uncountable/types/chemical_structure_t.py,sha256=zDJ6WkeT3YwWZRZT21znQn2ZYelv3L7yv7kJiGoNZCw,824
|
|
135
|
-
uncountable/types/client_base.py,sha256=
|
|
135
|
+
uncountable/types/client_base.py,sha256=qTifyQNBOwlspjWccQXg4gOhuwyQDy5z77rlreDynBU,67603
|
|
136
136
|
uncountable/types/client_config.py,sha256=4h5Liko9uKCo9_0gdbPhoK6Jr2Kv7tioLiQ8iKeq-_4,301
|
|
137
137
|
uncountable/types/client_config_t.py,sha256=6dStfR0IEHiPW8f9_aF3DD_tHmXXw2rEVrgpebzq8Fg,747
|
|
138
138
|
uncountable/types/curves.py,sha256=W6uMpG5SyW1MS82szNpxkFEn1MnxNpBFyFbQb2Ysfng,366
|
|
139
139
|
uncountable/types/curves_t.py,sha256=lKhRM-2cZ_sFaW7pa_I_Ipz_pJhm3_yTFehRXI79pKk,1416
|
|
140
140
|
uncountable/types/entity.py,sha256=3XhLteFDRDZvHejDuYh-KvB65hpwrBygljFfiUcOAM8,315
|
|
141
|
-
uncountable/types/entity_t.py,sha256=
|
|
141
|
+
uncountable/types/entity_t.py,sha256=8OkFVgvrItdA1ysyWB21mLL85JTPdcdzAmb6CNBUVI0,15003
|
|
142
142
|
uncountable/types/experiment_groups.py,sha256=_0OXcPzSAbkE-rfKt5tPx178YJ4pcEKZvrCxUHgDnvw,309
|
|
143
143
|
uncountable/types/experiment_groups_t.py,sha256=qEs8YW0eJOJ_sCOObT5v9QRx9wsjLYpJqJhCJXa-vNA,721
|
|
144
144
|
uncountable/types/field_values.py,sha256=uuIWX-xmfvcinYPdfkWJeb56zzQY01mc9rmotMPMh24,503
|
|
@@ -204,8 +204,8 @@ uncountable/types/api/batch/execute_batch_load_async.py,sha256=j5a5dk0_lTJ-YslrB
|
|
|
204
204
|
uncountable/types/api/chemical/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
|
|
205
205
|
uncountable/types/api/chemical/convert_chemical_formats.py,sha256=xLpma1W1O9MzgxM4CCl5GPnpj3dpqRHhKcXr3b_ToAo,1589
|
|
206
206
|
uncountable/types/api/entity/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
|
|
207
|
-
uncountable/types/api/entity/create_entities.py,sha256=
|
|
208
|
-
uncountable/types/api/entity/create_entity.py,sha256=
|
|
207
|
+
uncountable/types/api/entity/create_entities.py,sha256=hG81k_nKD4orTtb5jKIkCarkSbUa3fXAxES6IZVkhL0,2276
|
|
208
|
+
uncountable/types/api/entity/create_entity.py,sha256=BV99ZfkxPfMEGD0YvuMizK25Nll0o0GFho8_Mzb0Clk,2388
|
|
209
209
|
uncountable/types/api/entity/get_entities_data.py,sha256=gTEZ7Z7T-DWP8BZPNDF4c__EHtf9kAb1sGtHmiGOgnM,1454
|
|
210
210
|
uncountable/types/api/entity/list_entities.py,sha256=ykbdq4DD31uiRz4i8LH-8LLeA2Lpp_5fWfb5fdyx248,2000
|
|
211
211
|
uncountable/types/api/entity/lock_entity.py,sha256=mMZx2tWOtuYg0sIftdPsFWgZO5LCav2ubqTw97dCtDU,1197
|
|
@@ -276,8 +276,8 @@ uncountable/types/api/recipes/unlock_recipes.py,sha256=RaC5N5rz6f3FAIaQM3NgLuXEM
|
|
|
276
276
|
uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
|
|
277
277
|
uncountable/types/api/triggers/run_trigger.py,sha256=-oZgPyn43xEKSCs81DVNzwaYMCdRJxbM9GY6fsqKwf4,1090
|
|
278
278
|
uncountable/types/api/uploader/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
|
|
279
|
-
uncountable/types/api/uploader/invoke_uploader.py,sha256=
|
|
280
|
-
UncountablePythonSDK-0.0.
|
|
281
|
-
UncountablePythonSDK-0.0.
|
|
282
|
-
UncountablePythonSDK-0.0.
|
|
283
|
-
UncountablePythonSDK-0.0.
|
|
279
|
+
uncountable/types/api/uploader/invoke_uploader.py,sha256=6mwVG136oLp9JcbB2I-kZnrcm3aeZzYZB-SFjEImY2o,1314
|
|
280
|
+
UncountablePythonSDK-0.0.75.dist-info/METADATA,sha256=__fXg5O7uTKPPzAFToY3pOV8EHnNl6uX7UWCNat0E8Y,2051
|
|
281
|
+
UncountablePythonSDK-0.0.75.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
282
|
+
UncountablePythonSDK-0.0.75.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
|
|
283
|
+
UncountablePythonSDK-0.0.75.dist-info/RECORD,,
|
pkgs/argument_parser/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .argument_parser import CachedParser as CachedParser
|
|
2
|
+
from .argument_parser import ParserFunction as ParserFunction
|
|
2
3
|
from .argument_parser import ParserOptions as ParserOptions
|
|
3
4
|
from .argument_parser import build_parser as build_parser
|
|
4
5
|
from .case_convert import camel_to_snake_case as camel_to_snake_case
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
+
import math
|
|
2
3
|
import types
|
|
3
4
|
import typing
|
|
4
5
|
from collections import defaultdict
|
|
@@ -299,7 +300,18 @@ def _build_parser_inner(
|
|
|
299
300
|
|
|
300
301
|
return parse_str
|
|
301
302
|
|
|
302
|
-
if parsed_type in (float,
|
|
303
|
+
if parsed_type in (float, Decimal):
|
|
304
|
+
|
|
305
|
+
def parse_as_numeric_type(value: typing.Any) -> T:
|
|
306
|
+
numeric_value: Decimal | float = parsed_type(value) # type: ignore
|
|
307
|
+
if math.isnan(numeric_value):
|
|
308
|
+
raise ValueError(f"Invalid numeric value: {numeric_value}")
|
|
309
|
+
|
|
310
|
+
return numeric_value # type: ignore
|
|
311
|
+
|
|
312
|
+
return parse_as_numeric_type
|
|
313
|
+
|
|
314
|
+
if parsed_type in (dict, bool) or is_string_enum_class(parsed_type):
|
|
303
315
|
return lambda value: parsed_type(value) # type: ignore
|
|
304
316
|
|
|
305
317
|
if parsed_type is MissingSentryType:
|
|
@@ -15,6 +15,10 @@ class _SerialClassData:
|
|
|
15
15
|
to_string_values: set[str] = dataclasses.field(default_factory=set)
|
|
16
16
|
parse_require: set[str] = dataclasses.field(default_factory=set)
|
|
17
17
|
named_type_path: Optional[str] = None
|
|
18
|
+
# Tracks if this data was provided as a decorator to the type.
|
|
19
|
+
# This is used to track "proper types" which are appropriate
|
|
20
|
+
# for serialization and/or dynamic discovery
|
|
21
|
+
from_decorator: bool = False
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
EMPTY_SERIAL_CLASS_DATA = _SerialClassData()
|
|
@@ -58,6 +62,7 @@ def serial_class(
|
|
|
58
62
|
to_string_values=to_string_values or set(),
|
|
59
63
|
parse_require=parse_require or set(),
|
|
60
64
|
named_type_path=named_type_path,
|
|
65
|
+
from_decorator=True,
|
|
61
66
|
)
|
|
62
67
|
return orig_class
|
|
63
68
|
|
|
@@ -83,6 +88,14 @@ class SerialClassDataInspector:
|
|
|
83
88
|
def has_parse_require(self, key: str) -> bool:
|
|
84
89
|
return key in self.current.parse_require
|
|
85
90
|
|
|
91
|
+
@property
|
|
92
|
+
def from_decorator(self) -> bool:
|
|
93
|
+
return self.current.from_decorator
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def named_type_path(self) -> Optional[str]:
|
|
97
|
+
return self.current.named_type_path
|
|
98
|
+
|
|
86
99
|
|
|
87
100
|
def _get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | None:
|
|
88
101
|
base_class_data = (
|
pkgs/type_spec/builder.py
CHANGED
|
@@ -307,6 +307,9 @@ class SpecTypeDefn(SpecType):
|
|
|
307
307
|
def is_base_type(self, type_: BaseTypeName) -> bool:
|
|
308
308
|
return self.is_base and self.name == type_
|
|
309
309
|
|
|
310
|
+
def can_process(self, builder: SpecBuilder, data: RawDict) -> bool:
|
|
311
|
+
return True
|
|
312
|
+
|
|
310
313
|
@abc.abstractmethod
|
|
311
314
|
def process(self, builder: SpecBuilder, data: RawDict) -> None: ...
|
|
312
315
|
|
|
@@ -666,15 +669,30 @@ class SpecTypeDefnStringEnum(SpecTypeDefn):
|
|
|
666
669
|
self.desc: str | None = None
|
|
667
670
|
self.sql_type_name: Optional[str] = None
|
|
668
671
|
self.emit_id_source = False
|
|
672
|
+
self.source_enums: list[SpecType] = []
|
|
673
|
+
|
|
674
|
+
def can_process(self, builder: SpecBuilder, data: dict[Any, Any]) -> bool:
|
|
675
|
+
source_enums = data.get("source_enums")
|
|
676
|
+
try:
|
|
677
|
+
for sub_type_str in source_enums or []:
|
|
678
|
+
sub_type = builder.parse_type(self.namespace, sub_type_str)
|
|
679
|
+
assert isinstance(sub_type, SpecTypeDefnStringEnum)
|
|
680
|
+
assert len(sub_type.values) > 0
|
|
681
|
+
except AssertionError:
|
|
682
|
+
return False
|
|
683
|
+
return super().can_process(builder, data)
|
|
669
684
|
|
|
670
685
|
def process(self, builder: SpecBuilder, data: RawDict) -> None:
|
|
671
686
|
super().base_process(
|
|
672
|
-
builder,
|
|
687
|
+
builder,
|
|
688
|
+
data,
|
|
689
|
+
["type", "desc", "values", "name_case", "sql", "emit", "source_enums"],
|
|
673
690
|
)
|
|
674
691
|
self.name_case = NameCase(data.get("name_case", "convert"))
|
|
675
692
|
self.values = {}
|
|
676
|
-
data_values = data
|
|
693
|
+
data_values = data.get("values")
|
|
677
694
|
self.desc = data.get("desc", None)
|
|
695
|
+
source_enums = data.get("source_enums", None)
|
|
678
696
|
if isinstance(data_values, dict):
|
|
679
697
|
for name, value in data_values.items():
|
|
680
698
|
builder.push_where(name)
|
|
@@ -717,7 +735,8 @@ class SpecTypeDefnStringEnum(SpecTypeDefn):
|
|
|
717
735
|
)
|
|
718
736
|
self.values[value] = StringEnumEntry(name=value, value=value)
|
|
719
737
|
else:
|
|
720
|
-
|
|
738
|
+
if source_enums is None or data_values is not None:
|
|
739
|
+
raise Exception("unsupported values type")
|
|
721
740
|
|
|
722
741
|
sql_data = data.get("sql")
|
|
723
742
|
if sql_data is not None:
|
|
@@ -739,9 +758,18 @@ class SpecTypeDefnStringEnum(SpecTypeDefn):
|
|
|
739
758
|
builder.ensure(
|
|
740
759
|
entry.label is not None, f"need-label-for-id-source:{entry.name}"
|
|
741
760
|
)
|
|
761
|
+
for sub_type_str in source_enums or []:
|
|
762
|
+
sub_type = builder.parse_type(self.namespace, sub_type_str)
|
|
763
|
+
self.source_enums.append(sub_type)
|
|
764
|
+
|
|
765
|
+
for sub_type in self.source_enums:
|
|
766
|
+
builder.push_where(sub_type.name)
|
|
767
|
+
if isinstance(sub_type, SpecTypeDefnStringEnum):
|
|
768
|
+
self.values.update(sub_type.values)
|
|
769
|
+
builder.pop_where()
|
|
742
770
|
|
|
743
771
|
def get_referenced_types(self) -> list[SpecType]:
|
|
744
|
-
return
|
|
772
|
+
return self.source_enums
|
|
745
773
|
|
|
746
774
|
|
|
747
775
|
TOKEN_ENDPOINT = "$endpoint"
|
|
@@ -1122,28 +1150,41 @@ class SpecNamespace:
|
|
|
1122
1150
|
Complete the definition of each type.
|
|
1123
1151
|
"""
|
|
1124
1152
|
builder.push_where(self.name)
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1153
|
+
items_to_process: list[NameDataPair] = [
|
|
1154
|
+
NameDataPair(full_name=full_name, data=defn)
|
|
1155
|
+
for full_name, defn in data.items()
|
|
1156
|
+
]
|
|
1157
|
+
while len(items_to_process) > 0:
|
|
1158
|
+
deferred_items: list[NameDataPair] = []
|
|
1159
|
+
for item in items_to_process:
|
|
1160
|
+
full_name = item.full_name
|
|
1161
|
+
defn = item.data
|
|
1162
|
+
parsed_name = parse_type_str(full_name)[0]
|
|
1163
|
+
name = parsed_name.name
|
|
1164
|
+
|
|
1165
|
+
if name in [TOKEN_EMIT_IO_TS, TOKEN_EMIT_TYPE_INFO, TOKEN_IMPORT]:
|
|
1166
|
+
continue
|
|
1128
1167
|
|
|
1129
|
-
|
|
1130
|
-
continue
|
|
1168
|
+
builder.push_where(name)
|
|
1131
1169
|
|
|
1132
|
-
|
|
1170
|
+
if "value" in defn:
|
|
1171
|
+
spec_constant = self.constants[name]
|
|
1172
|
+
spec_constant.process(builder, defn)
|
|
1133
1173
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1174
|
+
elif name == TOKEN_ENDPOINT:
|
|
1175
|
+
assert self.endpoint
|
|
1176
|
+
self.endpoint.process(builder, defn)
|
|
1137
1177
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
spec_type.process(builder, defn)
|
|
1178
|
+
else:
|
|
1179
|
+
spec_type = self.types[name]
|
|
1180
|
+
if spec_type.can_process(builder, defn):
|
|
1181
|
+
spec_type.process(builder, defn)
|
|
1182
|
+
else:
|
|
1183
|
+
deferred_items.append(item)
|
|
1145
1184
|
|
|
1146
|
-
|
|
1185
|
+
builder.pop_where()
|
|
1186
|
+
assert len(deferred_items) < len(items_to_process)
|
|
1187
|
+
items_to_process = [deferred for deferred in deferred_items]
|
|
1147
1188
|
|
|
1148
1189
|
builder.pop_where()
|
|
1149
1190
|
|
|
@@ -1158,6 +1199,12 @@ class NamespaceDataPair:
|
|
|
1158
1199
|
data: RawDict
|
|
1159
1200
|
|
|
1160
1201
|
|
|
1202
|
+
@dataclass(kw_only=True)
|
|
1203
|
+
class NameDataPair:
|
|
1204
|
+
full_name: str
|
|
1205
|
+
data: RawDict
|
|
1206
|
+
|
|
1207
|
+
|
|
1161
1208
|
class SpecBuilder:
|
|
1162
1209
|
def __init__(self, *, api_endpoints: dict[str, str], top_namespace: str) -> None:
|
|
1163
1210
|
self.top_namespace = top_namespace
|
pkgs/type_spec/emit_python.py
CHANGED
|
@@ -44,7 +44,6 @@ class TrackingContext:
|
|
|
44
44
|
use_enum: bool = False
|
|
45
45
|
use_serial_string_enum: bool = False
|
|
46
46
|
use_dataclass: bool = False
|
|
47
|
-
use_serial_class: bool = False
|
|
48
47
|
use_serial_union: bool = False
|
|
49
48
|
use_missing: bool = False
|
|
50
49
|
use_opaque_key: bool = False
|
|
@@ -223,7 +222,6 @@ def _emit_types_imports(*, out: io.StringIO, ctx: Context) -> None:
|
|
|
223
222
|
out.write("from pkgs.strenum_compat import StrEnum\n")
|
|
224
223
|
if ctx.use_dataclass:
|
|
225
224
|
out.write("import dataclasses\n")
|
|
226
|
-
if ctx.use_serial_class:
|
|
227
225
|
out.write("from pkgs.serialization import serial_class\n")
|
|
228
226
|
if ctx.use_serial_union:
|
|
229
227
|
out.write("from pkgs.serialization import serial_union_annotation\n")
|
|
@@ -834,7 +832,6 @@ def _emit_type(ctx: Context, stype: builder.SpecType) -> None:
|
|
|
834
832
|
_emit_generic(ctx, stype.get_generic())
|
|
835
833
|
|
|
836
834
|
# Emit serial_class decorator
|
|
837
|
-
ctx.use_serial_class = True
|
|
838
835
|
ctx.out.write("@serial_class(\n")
|
|
839
836
|
ctx.out.write(
|
|
840
837
|
f"{INDENT}named_type_path={util.encode_common_string(_named_type_path(ctx, stype))},\n"
|
|
@@ -1,82 +1,21 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import os
|
|
3
|
-
from typing import Any
|
|
4
3
|
|
|
5
4
|
from . import builder, util
|
|
6
|
-
from .builder import SpecTypeDefnObject
|
|
7
5
|
from .config import TypeScriptConfig
|
|
8
6
|
from .emit_io_ts import emit_type_io_ts
|
|
9
7
|
from .emit_typescript_util import (
|
|
10
|
-
INDENT,
|
|
11
8
|
MODIFY_NOTICE,
|
|
12
9
|
EmitTypescriptContext,
|
|
10
|
+
emit_namespace_imports_ts,
|
|
11
|
+
emit_type_ts,
|
|
12
|
+
emit_value_ts,
|
|
13
|
+
resolve_namespace_name,
|
|
13
14
|
resolve_namespace_ref,
|
|
14
|
-
ts_name,
|
|
15
15
|
ts_type_name,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def ts_enum_name(name: str, name_case: builder.NameCase) -> str:
|
|
20
|
-
if name_case == builder.NameCase.js_upper:
|
|
21
|
-
return name.upper()
|
|
22
|
-
return ts_name(name, name_case)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _resolve_namespace_name(namespace: builder.SpecNamespace) -> str:
|
|
26
|
-
return namespace.name
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _emit_value(ctx: EmitTypescriptContext, stype: builder.SpecType, value: Any) -> str:
|
|
30
|
-
"""Mimics emit_python even if not all types are used in TypeScript yet"""
|
|
31
|
-
literal = builder.unwrap_literal_type(stype)
|
|
32
|
-
if literal is not None:
|
|
33
|
-
return _emit_value(ctx, literal.value_type, literal.value)
|
|
34
|
-
|
|
35
|
-
if stype.is_base_type(builder.BaseTypeName.s_string):
|
|
36
|
-
assert isinstance(value, str)
|
|
37
|
-
return util.encode_common_string(value)
|
|
38
|
-
elif stype.is_base_type(builder.BaseTypeName.s_integer):
|
|
39
|
-
assert isinstance(value, int)
|
|
40
|
-
return str(value)
|
|
41
|
-
elif stype.is_base_type(builder.BaseTypeName.s_boolean):
|
|
42
|
-
assert isinstance(value, bool)
|
|
43
|
-
return "true" if value else "false"
|
|
44
|
-
elif stype.is_base_type(builder.BaseTypeName.s_lossy_decimal):
|
|
45
|
-
return str(value)
|
|
46
|
-
elif stype.is_base_type(builder.BaseTypeName.s_decimal):
|
|
47
|
-
return f"'{value}'"
|
|
48
|
-
elif isinstance(stype, builder.SpecTypeInstance):
|
|
49
|
-
if stype.defn_type.is_base_type(builder.BaseTypeName.s_list):
|
|
50
|
-
sub_type = stype.parameters[0]
|
|
51
|
-
return "[" + ", ".join([_emit_value(ctx, sub_type, x) for x in value]) + "]"
|
|
52
|
-
|
|
53
|
-
if stype.defn_type.is_base_type(builder.BaseTypeName.s_dict):
|
|
54
|
-
key_type = stype.parameters[0]
|
|
55
|
-
value_type = stype.parameters[1]
|
|
56
|
-
return (
|
|
57
|
-
"{\n\t"
|
|
58
|
-
+ ",\n\t".join(
|
|
59
|
-
"["
|
|
60
|
-
+ _emit_value(ctx, key_type, dkey)
|
|
61
|
-
+ "]: "
|
|
62
|
-
+ _emit_value(ctx, value_type, dvalue)
|
|
63
|
-
for dkey, dvalue in value.items()
|
|
64
|
-
)
|
|
65
|
-
+ "\n}"
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
if stype.defn_type.is_base_type(builder.BaseTypeName.s_optional):
|
|
69
|
-
sub_type = stype.parameters[0]
|
|
70
|
-
if value is None:
|
|
71
|
-
return "null"
|
|
72
|
-
return _emit_value(ctx, sub_type, value)
|
|
73
|
-
|
|
74
|
-
elif isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
75
|
-
return f"{refer_to(ctx, stype)}.{ts_enum_name(value, stype.name_case)}"
|
|
76
|
-
|
|
77
|
-
raise Exception("invalid constant type", value, stype)
|
|
78
|
-
|
|
79
|
-
|
|
80
19
|
def emit_typescript(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
81
20
|
_emit_types(builder, config)
|
|
82
21
|
_emit_id_source(builder, config)
|
|
@@ -90,13 +29,11 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
90
29
|
|
|
91
30
|
for namespace in sorted(
|
|
92
31
|
builder.namespaces.values(),
|
|
93
|
-
key=lambda ns:
|
|
32
|
+
key=lambda ns: resolve_namespace_name(ns),
|
|
94
33
|
):
|
|
95
|
-
ctx = EmitTypescriptContext(
|
|
96
|
-
out=io.StringIO(), namespace=namespace, config=config
|
|
97
|
-
)
|
|
34
|
+
ctx = EmitTypescriptContext(out=io.StringIO(), namespace=namespace)
|
|
98
35
|
|
|
99
|
-
_emit_namespace(ctx, namespace)
|
|
36
|
+
_emit_namespace(ctx, config, namespace)
|
|
100
37
|
|
|
101
38
|
prepart = builder.preparts["typescript"].get(namespace.name)
|
|
102
39
|
part = builder.parts["typescript"].get(namespace.name)
|
|
@@ -123,16 +60,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
123
60
|
full.write(f"// === END section from {namespace.name}.ts.prepart ===\n")
|
|
124
61
|
full.write("\n")
|
|
125
62
|
|
|
126
|
-
|
|
127
|
-
ctx.namespaces,
|
|
128
|
-
key=lambda name: _resolve_namespace_name(name),
|
|
129
|
-
):
|
|
130
|
-
import_as = resolve_namespace_ref(ns)
|
|
131
|
-
import_path = (
|
|
132
|
-
"./" if len(namespace.path) == 1 else "../" * (len(namespace.path) - 1)
|
|
133
|
-
)
|
|
134
|
-
import_from = f"{import_path}{_resolve_namespace_name(ns)}"
|
|
135
|
-
full.write(f'import * as {import_as} from "{import_from}"\n') # noqa: E501
|
|
63
|
+
emit_namespace_imports_ts(ctx.namespaces, out=full, current_namespace=namespace)
|
|
136
64
|
if namespace.emit_io_ts:
|
|
137
65
|
full.write("import * as IO from 'io-ts';")
|
|
138
66
|
full.write(ctx.out.getvalue())
|
|
@@ -152,7 +80,7 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
152
80
|
|
|
153
81
|
if len(namespace.path) == 1:
|
|
154
82
|
index_out.write(
|
|
155
|
-
f"import * as {resolve_namespace_ref(namespace)} from './{
|
|
83
|
+
f"import * as {resolve_namespace_ref(namespace)} from './{resolve_namespace_name(namespace)}'\n"
|
|
156
84
|
) # noqa: E501
|
|
157
85
|
index_out_end.write(f"export {{{resolve_namespace_ref(namespace)}}}\n")
|
|
158
86
|
|
|
@@ -164,23 +92,26 @@ def _emit_types(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
|
164
92
|
|
|
165
93
|
|
|
166
94
|
def _emit_namespace(
|
|
167
|
-
ctx: EmitTypescriptContext,
|
|
95
|
+
ctx: EmitTypescriptContext,
|
|
96
|
+
config: TypeScriptConfig,
|
|
97
|
+
namespace: builder.SpecNamespace,
|
|
168
98
|
) -> None:
|
|
169
99
|
for stype in namespace.types.values():
|
|
170
100
|
if namespace.emit_io_ts:
|
|
171
101
|
emit_type_io_ts(ctx, stype, namespace.derive_types_from_io_ts)
|
|
172
102
|
if not namespace.emit_io_ts or not namespace.derive_types_from_io_ts:
|
|
173
|
-
|
|
103
|
+
emit_type_ts(ctx, stype)
|
|
174
104
|
|
|
175
105
|
for sconst in namespace.constants.values():
|
|
176
106
|
_emit_constant(ctx, sconst)
|
|
177
107
|
|
|
178
108
|
if namespace.endpoint is not None:
|
|
179
|
-
_emit_endpoint(ctx, namespace, namespace.endpoint)
|
|
109
|
+
_emit_endpoint(ctx, config, namespace, namespace.endpoint)
|
|
180
110
|
|
|
181
111
|
|
|
182
112
|
def _emit_endpoint(
|
|
183
113
|
ctx: EmitTypescriptContext,
|
|
114
|
+
config: TypeScriptConfig,
|
|
184
115
|
namespace: builder.SpecNamespace,
|
|
185
116
|
endpoint: builder.SpecEndpoint,
|
|
186
117
|
) -> None:
|
|
@@ -265,14 +196,12 @@ export const apiCall = {wrap_call}(
|
|
|
265
196
|
)
|
|
266
197
|
{data_loader_body}"""
|
|
267
198
|
|
|
268
|
-
output = f"{
|
|
199
|
+
output = f"{config.routes_output}/{"/".join(namespace.path)}.tsx"
|
|
269
200
|
util.rewrite_file(output, tsx_api)
|
|
270
201
|
|
|
271
202
|
# Hacky index support, until enough is migrated to regen entirely
|
|
272
203
|
# Emits the import into the UI API index file
|
|
273
|
-
index_path = (
|
|
274
|
-
f"{ctx.config.routes_output}/{"/".join(namespace.path[0:-1])}/index.tsx"
|
|
275
|
-
)
|
|
204
|
+
index_path = f"{config.routes_output}/{"/".join(namespace.path[0:-1])}/index.tsx"
|
|
276
205
|
api_name = f"Api{ts_type_name(namespace.path[0 - 1])}"
|
|
277
206
|
if os.path.exists(index_path):
|
|
278
207
|
with open(index_path) as index:
|
|
@@ -288,164 +217,14 @@ export const apiCall = {wrap_call}(
|
|
|
288
217
|
index.write(f"export {{ {api_name} }}\n")
|
|
289
218
|
|
|
290
219
|
|
|
291
|
-
def _emit_type(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
|
|
292
|
-
if not isinstance(stype, builder.SpecTypeDefn):
|
|
293
|
-
return
|
|
294
|
-
|
|
295
|
-
if stype.is_base or stype.is_predefined:
|
|
296
|
-
return
|
|
297
|
-
|
|
298
|
-
ctx.out.write("\n")
|
|
299
|
-
ctx.out.write(MODIFY_NOTICE)
|
|
300
|
-
|
|
301
|
-
if isinstance(stype, builder.SpecTypeDefnExternal):
|
|
302
|
-
assert not stype.is_exported, "expecting private names"
|
|
303
|
-
ctx.out.write(stype.external_map["ts"])
|
|
304
|
-
ctx.out.write("\n")
|
|
305
|
-
return
|
|
306
|
-
|
|
307
|
-
assert stype.is_exported, "expecting exported names"
|
|
308
|
-
if isinstance(stype, builder.SpecTypeDefnAlias):
|
|
309
|
-
ctx.out.write(f"export type {stype.name} = {refer_to(ctx, stype.alias)}\n")
|
|
310
|
-
return
|
|
311
|
-
|
|
312
|
-
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
313
|
-
ctx.out.write(
|
|
314
|
-
f"export type {stype.name} = {refer_to(ctx, stype.get_backing_type())}\n"
|
|
315
|
-
)
|
|
316
|
-
return
|
|
317
|
-
|
|
318
|
-
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
319
|
-
ctx.out.write(f"export enum {stype.name} {{\n")
|
|
320
|
-
assert stype.values
|
|
321
|
-
for name, entry in stype.values.items():
|
|
322
|
-
ctx.out.write(
|
|
323
|
-
f'{INDENT}{ts_enum_name(name, stype.name_case)} = "{entry.value}",\n'
|
|
324
|
-
)
|
|
325
|
-
ctx.out.write("}\n")
|
|
326
|
-
return
|
|
327
|
-
|
|
328
|
-
assert isinstance(stype, builder.SpecTypeDefnObject)
|
|
329
|
-
assert stype.base is not None
|
|
330
|
-
|
|
331
|
-
base_type = ""
|
|
332
|
-
if not stype.base.is_base:
|
|
333
|
-
base_type = f"{refer_to(ctx, stype.base)} & "
|
|
334
|
-
|
|
335
|
-
if stype.properties is None and base_type == "":
|
|
336
|
-
ctx.out.write(f"export type {stype.name} = TEmpty\n")
|
|
337
|
-
elif stype.properties is None:
|
|
338
|
-
ctx.out.write(f"export type {stype.name} = {base_type}{{}}\n")
|
|
339
|
-
else:
|
|
340
|
-
if isinstance(stype, SpecTypeDefnObject) and len(stype.parameters) > 0:
|
|
341
|
-
full_type_name = f'{stype.name}<{", ".join(stype.parameters)}>'
|
|
342
|
-
else:
|
|
343
|
-
full_type_name = stype.name
|
|
344
|
-
ctx.out.write(f"export type {full_type_name} = {base_type}{{")
|
|
345
|
-
ctx.out.write("\n")
|
|
346
|
-
for prop in stype.properties.values():
|
|
347
|
-
ref_type = refer_to(ctx, prop.spec_type)
|
|
348
|
-
prop_name = ts_name(prop.name, prop.name_case)
|
|
349
|
-
if prop.has_default and not prop.parse_require:
|
|
350
|
-
# For now, we'll assume the generated types with defaults are meant as
|
|
351
|
-
# arguments, thus treat like extant==missing
|
|
352
|
-
# IMPROVE: if we can decide they are meant as output instead, then
|
|
353
|
-
# they should be marked as required
|
|
354
|
-
ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
|
|
355
|
-
elif prop.extant == builder.PropertyExtant.missing:
|
|
356
|
-
# Unlike optional below, missing does not imply null is possible. They
|
|
357
|
-
# treated distinctly.
|
|
358
|
-
ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
|
|
359
|
-
elif prop.extant == builder.PropertyExtant.optional:
|
|
360
|
-
# Need to add in |null since Python side can produce null's right now
|
|
361
|
-
# IMPROVE: It would be better if the serializer could instead omit the None's
|
|
362
|
-
# Dropping the null should be forward compatible
|
|
363
|
-
ctx.out.write(f"{INDENT}{prop_name}?: {ref_type} | null")
|
|
364
|
-
else:
|
|
365
|
-
ctx.out.write(f"{INDENT}{prop_name}: {ref_type}")
|
|
366
|
-
ctx.out.write("\n")
|
|
367
|
-
ctx.out.write("}\n")
|
|
368
|
-
|
|
369
|
-
|
|
370
220
|
def _emit_constant(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -> None:
|
|
371
221
|
ctx.out.write("\n\n")
|
|
372
222
|
ctx.out.write(MODIFY_NOTICE)
|
|
373
|
-
value =
|
|
223
|
+
value = emit_value_ts(ctx, sconst.value_type, sconst.value)
|
|
374
224
|
const_name = sconst.name.upper()
|
|
375
225
|
ctx.out.write(f"export const {const_name} = {value}\n")
|
|
376
226
|
|
|
377
227
|
|
|
378
|
-
base_name_map = {
|
|
379
|
-
builder.BaseTypeName.s_boolean: "boolean",
|
|
380
|
-
builder.BaseTypeName.s_date: "string", # IMPROVE: Aliased DateStr
|
|
381
|
-
builder.BaseTypeName.s_date_time: "string", # IMPROVE: Aliased DateTimeStr
|
|
382
|
-
# Decimal's are marked as to_string_values thus are strings in the front-end
|
|
383
|
-
builder.BaseTypeName.s_decimal: "string",
|
|
384
|
-
builder.BaseTypeName.s_dict: "PartialRecord",
|
|
385
|
-
builder.BaseTypeName.s_integer: "number",
|
|
386
|
-
builder.BaseTypeName.s_lossy_decimal: "number",
|
|
387
|
-
builder.BaseTypeName.s_opaque_key: "string",
|
|
388
|
-
builder.BaseTypeName.s_none: "null",
|
|
389
|
-
builder.BaseTypeName.s_string: "string",
|
|
390
|
-
# UNC: global types
|
|
391
|
-
builder.BaseTypeName.s_json_value: "JsonValue",
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
def refer_to(ctx: EmitTypescriptContext, stype: builder.SpecType) -> str:
|
|
396
|
-
return refer_to_impl(ctx, stype)[0]
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
def refer_to_impl(
|
|
400
|
-
ctx: EmitTypescriptContext, stype: builder.SpecType
|
|
401
|
-
) -> tuple[str, bool]:
|
|
402
|
-
"""
|
|
403
|
-
@return (string-specific, multiple-types)
|
|
404
|
-
"""
|
|
405
|
-
if isinstance(stype, builder.SpecTypeInstance):
|
|
406
|
-
if stype.defn_type.name == builder.BaseTypeName.s_list:
|
|
407
|
-
spec, multi = refer_to_impl(ctx, stype.parameters[0])
|
|
408
|
-
return f"({spec})[]" if multi else f"{spec}[]", False
|
|
409
|
-
if stype.defn_type.name == builder.BaseTypeName.s_readonly_array:
|
|
410
|
-
spec, multi = refer_to_impl(ctx, stype.parameters[0])
|
|
411
|
-
return f"readonly ({spec})[]" if multi else f"readonly {spec}[]", False
|
|
412
|
-
if stype.defn_type.name == builder.BaseTypeName.s_union:
|
|
413
|
-
return (
|
|
414
|
-
f'({" | ".join([refer_to(ctx, p) for p in stype.parameters])})',
|
|
415
|
-
False,
|
|
416
|
-
)
|
|
417
|
-
if stype.defn_type.name == builder.BaseTypeName.s_literal:
|
|
418
|
-
parts = []
|
|
419
|
-
for parameter in stype.parameters:
|
|
420
|
-
assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
|
|
421
|
-
parts.append(refer_to(ctx, parameter))
|
|
422
|
-
return f'({" | ".join(parts)})', False
|
|
423
|
-
if stype.defn_type.name == builder.BaseTypeName.s_optional:
|
|
424
|
-
return f"{refer_to(ctx, stype.parameters[0])} | null", True
|
|
425
|
-
if stype.defn_type.name == builder.BaseTypeName.s_tuple:
|
|
426
|
-
return f"[{", ".join([refer_to(ctx, p) for p in stype.parameters])}]", False
|
|
427
|
-
params = ", ".join([refer_to(ctx, p) for p in stype.parameters])
|
|
428
|
-
return f"{refer_to(ctx, stype.defn_type)}<{params}>", False
|
|
429
|
-
|
|
430
|
-
if isinstance(stype, builder.SpecTypeLiteralWrapper):
|
|
431
|
-
return _emit_value(ctx, stype.value_type, stype.value), False
|
|
432
|
-
|
|
433
|
-
if isinstance(stype, builder.SpecTypeGenericParameter):
|
|
434
|
-
return stype.name, False
|
|
435
|
-
|
|
436
|
-
assert isinstance(stype, builder.SpecTypeDefn)
|
|
437
|
-
if stype.is_base: # assume correct namespace
|
|
438
|
-
if stype.name == builder.BaseTypeName.s_list:
|
|
439
|
-
return "any[]", False # TODO: generic type
|
|
440
|
-
return base_name_map[builder.BaseTypeName(stype.name)], False
|
|
441
|
-
|
|
442
|
-
if stype.namespace == ctx.namespace:
|
|
443
|
-
return stype.name, False
|
|
444
|
-
|
|
445
|
-
ctx.namespaces.add(stype.namespace)
|
|
446
|
-
return f"{resolve_namespace_ref(stype.namespace)}.{stype.name}", False
|
|
447
|
-
|
|
448
|
-
|
|
449
228
|
def _emit_id_source(builder: builder.SpecBuilder, config: TypeScriptConfig) -> None:
|
|
450
229
|
id_source_output = config.id_source_output
|
|
451
230
|
if id_source_output is None:
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
import io
|
|
2
|
+
import typing
|
|
2
3
|
from dataclasses import dataclass, field
|
|
3
4
|
|
|
4
5
|
from . import builder, util
|
|
5
|
-
from .config import TypeScriptConfig
|
|
6
6
|
|
|
7
7
|
INDENT = " "
|
|
8
8
|
|
|
9
9
|
MODIFY_NOTICE = "// DO NOT MODIFY -- This file is generated by type_spec\n"
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
base_name_map = {
|
|
13
|
+
builder.BaseTypeName.s_boolean: "boolean",
|
|
14
|
+
builder.BaseTypeName.s_date: "string", # IMPROVE: Aliased DateStr
|
|
15
|
+
builder.BaseTypeName.s_date_time: "string", # IMPROVE: Aliased DateTimeStr
|
|
16
|
+
# Decimal's are marked as to_string_values thus are strings in the front-end
|
|
17
|
+
builder.BaseTypeName.s_decimal: "string",
|
|
18
|
+
builder.BaseTypeName.s_dict: "PartialRecord",
|
|
19
|
+
builder.BaseTypeName.s_integer: "number",
|
|
20
|
+
builder.BaseTypeName.s_lossy_decimal: "number",
|
|
21
|
+
builder.BaseTypeName.s_opaque_key: "string",
|
|
22
|
+
builder.BaseTypeName.s_none: "null",
|
|
23
|
+
builder.BaseTypeName.s_string: "string",
|
|
24
|
+
# UNC: global types
|
|
25
|
+
builder.BaseTypeName.s_json_value: "JsonValue",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
12
29
|
@dataclass(kw_only=True)
|
|
13
30
|
class EmitTypescriptContext:
|
|
14
|
-
config: TypeScriptConfig
|
|
15
31
|
out: io.StringIO
|
|
16
32
|
namespace: builder.SpecNamespace
|
|
17
33
|
namespaces: set[builder.SpecNamespace] = field(default_factory=set)
|
|
@@ -30,3 +46,220 @@ def ts_name(name: str, name_case: builder.NameCase) -> str:
|
|
|
30
46
|
return name
|
|
31
47
|
bits = util.split_any_name(name)
|
|
32
48
|
return "".join([bits[0], *[x.title() for x in bits[1:]]])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def emit_value_ts(
|
|
52
|
+
ctx: EmitTypescriptContext, stype: builder.SpecType, value: typing.Any
|
|
53
|
+
) -> str:
|
|
54
|
+
"""Mimics emit_python even if not all types are used in TypeScript yet"""
|
|
55
|
+
literal = builder.unwrap_literal_type(stype)
|
|
56
|
+
if literal is not None:
|
|
57
|
+
return emit_value_ts(ctx, literal.value_type, literal.value)
|
|
58
|
+
|
|
59
|
+
if stype.is_base_type(builder.BaseTypeName.s_string):
|
|
60
|
+
assert isinstance(value, str)
|
|
61
|
+
return util.encode_common_string(value)
|
|
62
|
+
elif stype.is_base_type(builder.BaseTypeName.s_integer):
|
|
63
|
+
assert isinstance(value, int)
|
|
64
|
+
return str(value)
|
|
65
|
+
elif stype.is_base_type(builder.BaseTypeName.s_boolean):
|
|
66
|
+
assert isinstance(value, bool)
|
|
67
|
+
return "true" if value else "false"
|
|
68
|
+
elif stype.is_base_type(builder.BaseTypeName.s_lossy_decimal):
|
|
69
|
+
return str(value)
|
|
70
|
+
elif stype.is_base_type(builder.BaseTypeName.s_decimal):
|
|
71
|
+
return f"'{value}'"
|
|
72
|
+
elif isinstance(stype, builder.SpecTypeInstance):
|
|
73
|
+
if stype.defn_type.is_base_type(builder.BaseTypeName.s_list):
|
|
74
|
+
sub_type = stype.parameters[0]
|
|
75
|
+
return (
|
|
76
|
+
"[" + ", ".join([emit_value_ts(ctx, sub_type, x) for x in value]) + "]"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if stype.defn_type.is_base_type(builder.BaseTypeName.s_dict):
|
|
80
|
+
key_type = stype.parameters[0]
|
|
81
|
+
value_type = stype.parameters[1]
|
|
82
|
+
return (
|
|
83
|
+
"{\n\t"
|
|
84
|
+
+ ",\n\t".join(
|
|
85
|
+
"["
|
|
86
|
+
+ emit_value_ts(ctx, key_type, dkey)
|
|
87
|
+
+ "]: "
|
|
88
|
+
+ emit_value_ts(ctx, value_type, dvalue)
|
|
89
|
+
for dkey, dvalue in value.items()
|
|
90
|
+
)
|
|
91
|
+
+ "\n}"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if stype.defn_type.is_base_type(builder.BaseTypeName.s_optional):
|
|
95
|
+
sub_type = stype.parameters[0]
|
|
96
|
+
if value is None:
|
|
97
|
+
return "null"
|
|
98
|
+
return emit_value_ts(ctx, sub_type, value)
|
|
99
|
+
|
|
100
|
+
elif isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
101
|
+
return f"{refer_to(ctx, stype)}.{ts_enum_name(value, stype.name_case)}"
|
|
102
|
+
|
|
103
|
+
raise Exception("invalid constant type", value, stype)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def emit_type_ts(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
|
|
107
|
+
if not isinstance(stype, builder.SpecTypeDefn):
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
if stype.is_base or stype.is_predefined:
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
ctx.out.write("\n")
|
|
114
|
+
ctx.out.write(MODIFY_NOTICE)
|
|
115
|
+
|
|
116
|
+
if isinstance(stype, builder.SpecTypeDefnExternal):
|
|
117
|
+
assert not stype.is_exported, "expecting private names"
|
|
118
|
+
ctx.out.write(stype.external_map["ts"])
|
|
119
|
+
ctx.out.write("\n")
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
assert stype.is_exported, "expecting exported names"
|
|
123
|
+
if isinstance(stype, builder.SpecTypeDefnAlias):
|
|
124
|
+
ctx.out.write(f"export type {stype.name} = {refer_to(ctx, stype.alias)}\n")
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
128
|
+
ctx.out.write(
|
|
129
|
+
f"export type {stype.name} = {refer_to(ctx, stype.get_backing_type())}\n"
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
134
|
+
ctx.out.write(f"export enum {stype.name} {{\n")
|
|
135
|
+
assert stype.values
|
|
136
|
+
for name, entry in stype.values.items():
|
|
137
|
+
ctx.out.write(
|
|
138
|
+
f'{INDENT}{ts_enum_name(name, stype.name_case)} = "{entry.value}",\n'
|
|
139
|
+
)
|
|
140
|
+
ctx.out.write("}\n")
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
assert isinstance(stype, builder.SpecTypeDefnObject)
|
|
144
|
+
assert stype.base is not None
|
|
145
|
+
|
|
146
|
+
base_type = ""
|
|
147
|
+
if not stype.base.is_base:
|
|
148
|
+
base_type = f"{refer_to(ctx, stype.base)} & "
|
|
149
|
+
|
|
150
|
+
if stype.properties is None and base_type == "":
|
|
151
|
+
ctx.out.write(f"export type {stype.name} = TEmpty\n")
|
|
152
|
+
elif stype.properties is None:
|
|
153
|
+
ctx.out.write(f"export type {stype.name} = {base_type}{{}}\n")
|
|
154
|
+
else:
|
|
155
|
+
if isinstance(stype, builder.SpecTypeDefnObject) and len(stype.parameters) > 0:
|
|
156
|
+
full_type_name = f'{stype.name}<{", ".join(stype.parameters)}>'
|
|
157
|
+
else:
|
|
158
|
+
full_type_name = stype.name
|
|
159
|
+
ctx.out.write(f"export type {full_type_name} = {base_type}{{")
|
|
160
|
+
ctx.out.write("\n")
|
|
161
|
+
for prop in stype.properties.values():
|
|
162
|
+
ref_type = refer_to(ctx, prop.spec_type)
|
|
163
|
+
prop_name = ts_name(prop.name, prop.name_case)
|
|
164
|
+
if prop.has_default and not prop.parse_require:
|
|
165
|
+
# For now, we'll assume the generated types with defaults are meant as
|
|
166
|
+
# arguments, thus treat like extant==missing
|
|
167
|
+
# IMPROVE: if we can decide they are meant as output instead, then
|
|
168
|
+
# they should be marked as required
|
|
169
|
+
ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
|
|
170
|
+
elif prop.extant == builder.PropertyExtant.missing:
|
|
171
|
+
# Unlike optional below, missing does not imply null is possible. They
|
|
172
|
+
# treated distinctly.
|
|
173
|
+
ctx.out.write(f"{INDENT}{prop_name}?: {ref_type}")
|
|
174
|
+
elif prop.extant == builder.PropertyExtant.optional:
|
|
175
|
+
# Need to add in |null since Python side can produce null's right now
|
|
176
|
+
# IMPROVE: It would be better if the serializer could instead omit the None's
|
|
177
|
+
# Dropping the null should be forward compatible
|
|
178
|
+
ctx.out.write(f"{INDENT}{prop_name}?: {ref_type} | null")
|
|
179
|
+
else:
|
|
180
|
+
ctx.out.write(f"{INDENT}{prop_name}: {ref_type}")
|
|
181
|
+
ctx.out.write("\n")
|
|
182
|
+
ctx.out.write("}\n")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def refer_to(ctx: EmitTypescriptContext, stype: builder.SpecType) -> str:
|
|
186
|
+
return refer_to_impl(ctx, stype)[0]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def refer_to_impl(
|
|
190
|
+
ctx: EmitTypescriptContext, stype: builder.SpecType
|
|
191
|
+
) -> tuple[str, bool]:
|
|
192
|
+
"""
|
|
193
|
+
@return (string-specific, multiple-types)
|
|
194
|
+
"""
|
|
195
|
+
if isinstance(stype, builder.SpecTypeInstance):
|
|
196
|
+
if stype.defn_type.name == builder.BaseTypeName.s_list:
|
|
197
|
+
spec, multi = refer_to_impl(ctx, stype.parameters[0])
|
|
198
|
+
return f"({spec})[]" if multi else f"{spec}[]", False
|
|
199
|
+
if stype.defn_type.name == builder.BaseTypeName.s_readonly_array:
|
|
200
|
+
spec, multi = refer_to_impl(ctx, stype.parameters[0])
|
|
201
|
+
return f"readonly ({spec})[]" if multi else f"readonly {spec}[]", False
|
|
202
|
+
if stype.defn_type.name == builder.BaseTypeName.s_union:
|
|
203
|
+
return (
|
|
204
|
+
f'({" | ".join([refer_to(ctx, p) for p in stype.parameters])})',
|
|
205
|
+
False,
|
|
206
|
+
)
|
|
207
|
+
if stype.defn_type.name == builder.BaseTypeName.s_literal:
|
|
208
|
+
parts = []
|
|
209
|
+
for parameter in stype.parameters:
|
|
210
|
+
assert isinstance(parameter, builder.SpecTypeLiteralWrapper)
|
|
211
|
+
parts.append(refer_to(ctx, parameter))
|
|
212
|
+
return f'({" | ".join(parts)})', False
|
|
213
|
+
if stype.defn_type.name == builder.BaseTypeName.s_optional:
|
|
214
|
+
return f"{refer_to(ctx, stype.parameters[0])} | null", True
|
|
215
|
+
if stype.defn_type.name == builder.BaseTypeName.s_tuple:
|
|
216
|
+
return f"[{", ".join([refer_to(ctx, p) for p in stype.parameters])}]", False
|
|
217
|
+
params = ", ".join([refer_to(ctx, p) for p in stype.parameters])
|
|
218
|
+
return f"{refer_to(ctx, stype.defn_type)}<{params}>", False
|
|
219
|
+
|
|
220
|
+
if isinstance(stype, builder.SpecTypeLiteralWrapper):
|
|
221
|
+
return emit_value_ts(ctx, stype.value_type, stype.value), False
|
|
222
|
+
|
|
223
|
+
if isinstance(stype, builder.SpecTypeGenericParameter):
|
|
224
|
+
return stype.name, False
|
|
225
|
+
|
|
226
|
+
assert isinstance(stype, builder.SpecTypeDefn)
|
|
227
|
+
if stype.is_base: # assume correct namespace
|
|
228
|
+
if stype.name == builder.BaseTypeName.s_list:
|
|
229
|
+
return "any[]", False # TODO: generic type
|
|
230
|
+
return base_name_map[builder.BaseTypeName(stype.name)], False
|
|
231
|
+
|
|
232
|
+
if stype.namespace == ctx.namespace:
|
|
233
|
+
return stype.name, False
|
|
234
|
+
|
|
235
|
+
ctx.namespaces.add(stype.namespace)
|
|
236
|
+
return f"{resolve_namespace_ref(stype.namespace)}.{stype.name}", False
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def ts_enum_name(name: str, name_case: builder.NameCase) -> str:
|
|
240
|
+
if name_case == builder.NameCase.js_upper:
|
|
241
|
+
return name.upper()
|
|
242
|
+
return ts_name(name, name_case)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def resolve_namespace_name(namespace: builder.SpecNamespace) -> str:
|
|
246
|
+
return namespace.name
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def emit_namespace_imports_ts(
|
|
250
|
+
namespaces: set[builder.SpecNamespace],
|
|
251
|
+
out: io.StringIO,
|
|
252
|
+
current_namespace: builder.SpecNamespace,
|
|
253
|
+
) -> None:
|
|
254
|
+
for ns in sorted(
|
|
255
|
+
namespaces,
|
|
256
|
+
key=lambda name: resolve_namespace_name(name),
|
|
257
|
+
):
|
|
258
|
+
import_as = resolve_namespace_ref(ns)
|
|
259
|
+
import_path = (
|
|
260
|
+
"./"
|
|
261
|
+
if len(current_namespace.path) == 1
|
|
262
|
+
else "../" * (len(current_namespace.path) - 1)
|
|
263
|
+
)
|
|
264
|
+
import_from = f"{import_path}{resolve_namespace_name(ns)}"
|
|
265
|
+
out.write(f'import * as {import_as} from "{import_from}"\n') # noqa: E501
|
|
@@ -5,7 +5,7 @@ import io
|
|
|
5
5
|
import json
|
|
6
6
|
from typing import Any, Optional, Union, cast
|
|
7
7
|
|
|
8
|
-
from main.base.types import data_t
|
|
8
|
+
from main.base.types import data_t, type_info_t
|
|
9
9
|
from main.base.types.base_t import PureJsonValue
|
|
10
10
|
from pkgs.argument_parser import CachedParser
|
|
11
11
|
from pkgs.serialization_util import (
|
|
@@ -17,7 +17,7 @@ from .. import builder, util
|
|
|
17
17
|
from ..emit_typescript_util import MODIFY_NOTICE, ts_name
|
|
18
18
|
from ..value_spec import convert_to_value_spec_type
|
|
19
19
|
|
|
20
|
-
ext_info_parser = CachedParser(
|
|
20
|
+
ext_info_parser = CachedParser(type_info_t.ExtInfo, strict_property_parsing=True)
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def type_path_of(stype: builder.SpecType) -> object: # NamePath
|
|
@@ -174,7 +174,7 @@ class InheritablePropertyParts:
|
|
|
174
174
|
|
|
175
175
|
label: Optional[str] = None
|
|
176
176
|
desc: Optional[str] = None
|
|
177
|
-
ext_info: Optional[
|
|
177
|
+
ext_info: Optional[type_info_t.ExtInfo] = None
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
def _extract_inheritable_property_parts(
|
|
@@ -201,7 +201,7 @@ def _extract_inheritable_property_parts(
|
|
|
201
201
|
elif base_parts.ext_info is None:
|
|
202
202
|
ext_info = local_ext_info
|
|
203
203
|
else:
|
|
204
|
-
ext_info =
|
|
204
|
+
ext_info = type_info_t.ExtInfo(
|
|
205
205
|
**(local_ext_info.__dict__ | base_parts.ext_info.__dict__)
|
|
206
206
|
)
|
|
207
207
|
|
|
@@ -214,7 +214,7 @@ ALL_FIELDS_GROUP = "*all_fields"
|
|
|
214
214
|
|
|
215
215
|
def _extract_and_validate_layout(
|
|
216
216
|
stype: builder.SpecTypeDefnObject,
|
|
217
|
-
ext_info:
|
|
217
|
+
ext_info: type_info_t.ExtInfo,
|
|
218
218
|
base_layout: ExtInfoLayout | None,
|
|
219
219
|
) -> ExtInfoLayout:
|
|
220
220
|
"""
|
|
@@ -264,7 +264,7 @@ def _extract_and_validate_layout(
|
|
|
264
264
|
|
|
265
265
|
def _validate_type_ext_info(
|
|
266
266
|
stype: builder.SpecTypeDefnObject,
|
|
267
|
-
) -> tuple[ExtInfoLayout | None, Optional[
|
|
267
|
+
) -> tuple[ExtInfoLayout | None, Optional[type_info_t.ExtInfo]]:
|
|
268
268
|
ext_info = _parse_ext_info(stype.ext_info)
|
|
269
269
|
if ext_info is None:
|
|
270
270
|
return None, None
|
|
@@ -364,7 +364,7 @@ def _build_map_type(
|
|
|
364
364
|
return None
|
|
365
365
|
|
|
366
366
|
|
|
367
|
-
def _parse_ext_info(in_ext: Any) -> Optional[
|
|
367
|
+
def _parse_ext_info(in_ext: Any) -> Optional[type_info_t.ExtInfo]:
|
|
368
368
|
if in_ext is None:
|
|
369
369
|
return None
|
|
370
370
|
assert isinstance(in_ext, dict)
|
pkgs/type_spec/util.py
CHANGED
|
@@ -159,7 +159,7 @@ def is_valid_property_name(name: str) -> bool:
|
|
|
159
159
|
def check_fields(data: dict[str, T], allowed: list[str]) -> None:
|
|
160
160
|
for key in data:
|
|
161
161
|
if key not in allowed:
|
|
162
|
-
raise Exception(f"unexpected-field: {key}")
|
|
162
|
+
raise Exception(f"unexpected-field: {key}. Allowed: {allowed}")
|
|
163
163
|
|
|
164
164
|
|
|
165
165
|
def split_any_name(name: str) -> list[str]:
|
|
@@ -86,23 +86,23 @@ def run_queued_job(
|
|
|
86
86
|
profile_metadata=job_details.profile_metadata,
|
|
87
87
|
job_definition=job_details.job_definition,
|
|
88
88
|
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
try:
|
|
90
|
+
client = construct_uncountable_client(
|
|
91
|
+
profile_meta=job_details.profile_metadata, job_logger=job_logger
|
|
92
|
+
)
|
|
93
|
+
batch_processor = AsyncBatchProcessor(client=client)
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
payload = _resolve_queued_job_payload(queued_job)
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
args = JobArguments(
|
|
98
|
+
job_definition=job_details.job_definition,
|
|
99
|
+
client=client,
|
|
100
|
+
batch_processor=batch_processor,
|
|
101
|
+
profile_metadata=job_details.profile_metadata,
|
|
102
|
+
logger=job_logger,
|
|
103
|
+
payload=payload,
|
|
104
|
+
)
|
|
104
105
|
|
|
105
|
-
try:
|
|
106
106
|
return execute_job(
|
|
107
107
|
args=args,
|
|
108
108
|
profile_metadata=job_details.profile_metadata,
|
|
@@ -41,7 +41,7 @@ class EntityToCreate:
|
|
|
41
41
|
@dataclasses.dataclass(kw_only=True)
|
|
42
42
|
class Arguments:
|
|
43
43
|
definition_id: base_t.ObjectId
|
|
44
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.OUTPUT]]
|
|
44
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.CONDITION_PARAMETER], typing.Literal[entity_t.EntityType.OUTPUT]]
|
|
45
45
|
entities_to_create: list[EntityToCreate]
|
|
46
46
|
|
|
47
47
|
|
|
@@ -44,7 +44,7 @@ class EntityFieldInitialValue:
|
|
|
44
44
|
@dataclasses.dataclass(kw_only=True)
|
|
45
45
|
class Arguments:
|
|
46
46
|
definition_id: base_t.ObjectId
|
|
47
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.OUTPUT]]
|
|
47
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.CONDITION_PARAMETER], typing.Literal[entity_t.EntityType.OUTPUT]]
|
|
48
48
|
field_values: typing.Optional[typing.Optional[list[field_values_t.FieldRefNameValue]]] = None
|
|
49
49
|
|
|
50
50
|
|
|
@@ -31,9 +31,10 @@ ENDPOINT_PATH = "api/external/uploader/invoke_uploader"
|
|
|
31
31
|
)
|
|
32
32
|
@dataclasses.dataclass(kw_only=True)
|
|
33
33
|
class Arguments:
|
|
34
|
-
file_id: base_t.ObjectId
|
|
35
34
|
uploader_key: identifier_t.IdentifierKey
|
|
36
35
|
destination: generic_upload_t.UploadDestination
|
|
36
|
+
file_id: typing.Optional[base_t.ObjectId] = None
|
|
37
|
+
file_ids: typing.Optional[list[base_t.ObjectId]] = None
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -233,17 +233,20 @@ class AsyncBatchProcessorBase(ABC):
|
|
|
233
233
|
def invoke_uploader(
|
|
234
234
|
self,
|
|
235
235
|
*,
|
|
236
|
-
file_id: base_t.ObjectId,
|
|
237
236
|
uploader_key: identifier_t.IdentifierKey,
|
|
238
237
|
destination: generic_upload_t.UploadDestination,
|
|
238
|
+
file_id: typing.Optional[base_t.ObjectId] = None,
|
|
239
|
+
file_ids: typing.Optional[list[base_t.ObjectId]] = None,
|
|
239
240
|
depends_on: typing.Optional[list[str]] = None,
|
|
240
241
|
) -> async_batch_t.QueuedAsyncBatchRequest:
|
|
241
242
|
"""Runs a file through an uploader.
|
|
242
243
|
|
|
244
|
+
:param file_id: DEPRECATED: use file_ids
|
|
243
245
|
:param depends_on: A list of batch reference keys to process before processing this request
|
|
244
246
|
"""
|
|
245
247
|
args = invoke_uploader_t.Arguments(
|
|
246
248
|
file_id=file_id,
|
|
249
|
+
file_ids=file_ids,
|
|
247
250
|
uploader_key=uploader_key,
|
|
248
251
|
destination=destination,
|
|
249
252
|
)
|
uncountable/types/client_base.py
CHANGED
|
@@ -257,7 +257,7 @@ class ClientMethods(ABC):
|
|
|
257
257
|
self,
|
|
258
258
|
*,
|
|
259
259
|
definition_id: base_t.ObjectId,
|
|
260
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.OUTPUT]],
|
|
260
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.CONDITION_PARAMETER], typing.Literal[entity_t.EntityType.OUTPUT]],
|
|
261
261
|
entities_to_create: list[create_entities_t.EntityToCreate],
|
|
262
262
|
) -> create_entities_t.Data:
|
|
263
263
|
"""Creates new Uncountable entities
|
|
@@ -282,7 +282,7 @@ class ClientMethods(ABC):
|
|
|
282
282
|
self,
|
|
283
283
|
*,
|
|
284
284
|
definition_id: base_t.ObjectId,
|
|
285
|
-
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.OUTPUT]],
|
|
285
|
+
entity_type: typing.Union[typing.Literal[entity_t.EntityType.LAB_REQUEST], typing.Literal[entity_t.EntityType.APPROVAL], typing.Literal[entity_t.EntityType.CUSTOM_ENTITY], typing.Literal[entity_t.EntityType.INVENTORY_AMOUNT], typing.Literal[entity_t.EntityType.TASK], typing.Literal[entity_t.EntityType.PROJECT], typing.Literal[entity_t.EntityType.EQUIPMENT], typing.Literal[entity_t.EntityType.INV_LOCAL_LOCATIONS], typing.Literal[entity_t.EntityType.FIELD_OPTION_SET], typing.Literal[entity_t.EntityType.WEBHOOK], typing.Literal[entity_t.EntityType.SPECS], typing.Literal[entity_t.EntityType.GOAL], typing.Literal[entity_t.EntityType.INGREDIENT_TAG_MAP], typing.Literal[entity_t.EntityType.INGREDIENT_TAG], typing.Literal[entity_t.EntityType.CONDITION_PARAMETER], typing.Literal[entity_t.EntityType.OUTPUT]],
|
|
286
286
|
field_values: typing.Optional[typing.Optional[list[field_values_t.FieldRefNameValue]]] = None,
|
|
287
287
|
) -> create_entity_t.Data:
|
|
288
288
|
"""Creates a new Uncountable entity
|
|
@@ -876,15 +876,18 @@ class ClientMethods(ABC):
|
|
|
876
876
|
def invoke_uploader(
|
|
877
877
|
self,
|
|
878
878
|
*,
|
|
879
|
-
file_id: base_t.ObjectId,
|
|
880
879
|
uploader_key: identifier_t.IdentifierKey,
|
|
881
880
|
destination: generic_upload_t.UploadDestination,
|
|
881
|
+
file_id: typing.Optional[base_t.ObjectId] = None,
|
|
882
|
+
file_ids: typing.Optional[list[base_t.ObjectId]] = None,
|
|
882
883
|
) -> invoke_uploader_t.Data:
|
|
883
884
|
"""Runs a file through an uploader.
|
|
884
885
|
|
|
886
|
+
:param file_id: DEPRECATED: use file_ids
|
|
885
887
|
"""
|
|
886
888
|
args = invoke_uploader_t.Arguments(
|
|
887
889
|
file_id=file_id,
|
|
890
|
+
file_ids=file_ids,
|
|
888
891
|
uploader_key=uploader_key,
|
|
889
892
|
destination=destination,
|
|
890
893
|
)
|
uncountable/types/entity_t.py
CHANGED
|
@@ -111,6 +111,7 @@ __all__: list[str] = [
|
|
|
111
111
|
"recipe_calculation": "Recipe Calculation",
|
|
112
112
|
"recipe_check": "Experiment Check",
|
|
113
113
|
"recipe_export": "Recipe Export",
|
|
114
|
+
"recipe_goal": "Experiment Goal",
|
|
114
115
|
"recipe_ingredient": "Recipe Ingredient",
|
|
115
116
|
"recipe_ingredient_actual": "Recipe Ingredient Actual",
|
|
116
117
|
"recipe_ingredients_compounded": "Recipe Ingredients Compounded",
|
|
@@ -265,6 +266,7 @@ class EntityType(StrEnum):
|
|
|
265
266
|
RECIPE_CALCULATION = "recipe_calculation"
|
|
266
267
|
RECIPE_CHECK = "recipe_check"
|
|
267
268
|
RECIPE_EXPORT = "recipe_export"
|
|
269
|
+
RECIPE_GOAL = "recipe_goal"
|
|
268
270
|
RECIPE_INGREDIENT = "recipe_ingredient"
|
|
269
271
|
RECIPE_INGREDIENT_ACTUAL = "recipe_ingredient_actual"
|
|
270
272
|
RECIPE_INGREDIENTS_COMPOUNDED = "recipe_ingredients_compounded"
|
{UncountablePythonSDK-0.0.73.dist-info → UncountablePythonSDK-0.0.75.dist-info}/top_level.txt
RENAMED
|
File without changes
|