UncountablePythonSDK 0.0.85__py3-none-any.whl → 0.0.87__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.87.dist-info/METADATA +61 -0
- {UncountablePythonSDK-0.0.85.dist-info → UncountablePythonSDK-0.0.87.dist-info}/RECORD +14 -13
- {UncountablePythonSDK-0.0.85.dist-info → UncountablePythonSDK-0.0.87.dist-info}/WHEEL +1 -1
- examples/integration-server/pyproject.toml +1 -1
- pkgs/filesystem_utils/_blob_session.py +137 -0
- pkgs/filesystem_utils/file_type_utils.py +20 -0
- pkgs/serialization/serial_class.py +0 -2
- pkgs/type_spec/actions_registry/emit_typescript.py +1 -3
- uncountable/integration/executors/executors.py +13 -2
- uncountable/integration/queue_runner/worker.py +1 -0
- uncountable/types/api/recipes/edit_recipe_inputs.py +66 -1
- uncountable/types/entity_t.py +4 -0
- uncountable/types/integration_server_t.py +5 -0
- UncountablePythonSDK-0.0.85.dist-info/METADATA +0 -60
- {UncountablePythonSDK-0.0.85.dist-info → UncountablePythonSDK-0.0.87.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: UncountablePythonSDK
|
|
3
|
+
Version: 0.0.87
|
|
4
|
+
Summary: Uncountable SDK
|
|
5
|
+
Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
|
|
7
|
+
Project-URL: Issues, https://github.com/uncountableinc/uncountable-python-sdk/issues
|
|
8
|
+
Keywords: uncountable,sdk,api,uncountable-sdk
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: aiotus==1.*
|
|
20
|
+
Requires-Dist: aiohttp==3.*
|
|
21
|
+
Requires-Dist: requests==2.*
|
|
22
|
+
Requires-Dist: SQLAlchemy>=1.4.0
|
|
23
|
+
Requires-Dist: APScheduler==3.*
|
|
24
|
+
Requires-Dist: python-dateutil==2.*
|
|
25
|
+
Requires-Dist: shelljob==0.*
|
|
26
|
+
Requires-Dist: PyYAML==6.*
|
|
27
|
+
Requires-Dist: google-api-python-client==2.*
|
|
28
|
+
Requires-Dist: tqdm==4.*
|
|
29
|
+
Requires-Dist: pysftp==0.*
|
|
30
|
+
Requires-Dist: opentelemetry-api==1.*
|
|
31
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.*
|
|
32
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.*
|
|
33
|
+
Requires-Dist: opentelemetry-sdk==1.*
|
|
34
|
+
Requires-Dist: paramiko==3.*
|
|
35
|
+
Requires-Dist: boto3==1.*
|
|
36
|
+
Requires-Dist: flask==3.*
|
|
37
|
+
Requires-Dist: simplejson==3.*
|
|
38
|
+
Requires-Dist: grpcio==1.67.1
|
|
39
|
+
Requires-Dist: protobuf>=4.21.1
|
|
40
|
+
Requires-Dist: azure-storage-blob==12.*
|
|
41
|
+
Provides-Extra: test
|
|
42
|
+
Requires-Dist: mypy==1.*; extra == "test"
|
|
43
|
+
Requires-Dist: ruff==0.*; extra == "test"
|
|
44
|
+
Requires-Dist: pytest==8.*; extra == "test"
|
|
45
|
+
Requires-Dist: coverage[toml]==7.*; extra == "test"
|
|
46
|
+
Requires-Dist: pytest-cov==5.*; extra == "test"
|
|
47
|
+
Requires-Dist: pytest-xdist==3.*; extra == "test"
|
|
48
|
+
|
|
49
|
+
# Uncountable Python SDK
|
|
50
|
+
|
|
51
|
+
## Documentation
|
|
52
|
+
[https://uncountableinc.github.io/uncountable-python-sdk/](https://uncountableinc.github.io/uncountable-python-sdk/)
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
Install from PyPI:
|
|
57
|
+
|
|
58
|
+
```console
|
|
59
|
+
pip install UncountablePythonSDK
|
|
60
|
+
```
|
|
61
|
+
|
|
@@ -21,7 +21,7 @@ examples/invoke_uploader.py,sha256=rEvmVY5TjigN_-4PTQdkjY-bC5DrYMcJgquyZ4Tt5FM,7
|
|
|
21
21
|
examples/set_recipe_metadata_file.py,sha256=oPBhMo9T17zj4YpJRkmVH9lknYcT8livph0KssxYtg4,1048
|
|
22
22
|
examples/set_recipe_output_file_sdk.py,sha256=Lz1amqppnWTX83z-C090wCJ4hcKmCD3kb-4v0uBRi0Y,782
|
|
23
23
|
examples/upload_files.py,sha256=tUfKFqiqwnw08OL5Y8_e4j5pSRhp94cFex8XTuVa_ig,487
|
|
24
|
-
examples/integration-server/pyproject.toml,sha256=
|
|
24
|
+
examples/integration-server/pyproject.toml,sha256=ziyEbSiTYlOk784euVJjuZ3UqIEENkPXuug5VTrkjms,9131
|
|
25
25
|
examples/integration-server/jobs/materials_auto/example_cron.py,sha256=7VVQ-UJsq3DbGpN3XPnorRVZYo-vCwbfSU3VVDluIzA,699
|
|
26
26
|
examples/integration-server/jobs/materials_auto/example_wh.py,sha256=Hx5nonavOh2L3JykDpI5bPqPu8L1wwhwekTUfTRgq9g,479
|
|
27
27
|
examples/integration-server/jobs/materials_auto/profile.yaml,sha256=XlOXSRplMJ13T6900pv1wDKxeE9V1hZZTMuvup1MiBM,896
|
|
@@ -33,16 +33,17 @@ pkgs/argument_parser/_is_namedtuple.py,sha256=Rjc1bKanIPPogl3qG5JPBxglG1TqWYOo1n
|
|
|
33
33
|
pkgs/argument_parser/argument_parser.py,sha256=XjWQdcWanfaCGdLx7c_yQtqbZp7db-KAWzw8sTpXOsY,17713
|
|
34
34
|
pkgs/argument_parser/case_convert.py,sha256=NuJLJUJRbyVb6_Slen4uqaStEHbcOS1d-hBBfDrrw-c,605
|
|
35
35
|
pkgs/filesystem_utils/__init__.py,sha256=NSsQrUCoGISBCqCCyq6_583sYHTVEQeDjDO8hvZn3ag,1261
|
|
36
|
+
pkgs/filesystem_utils/_blob_session.py,sha256=CtoB7PIocuZo8vvFIS_Rc-YR6KwzFB0rHUVPKFEbRAI,4862
|
|
36
37
|
pkgs/filesystem_utils/_gdrive_session.py,sha256=GJuZYJq1W4QQ_7OLvZIMK99FgRq8FxJHg6cMUx9prtA,11077
|
|
37
38
|
pkgs/filesystem_utils/_local_session.py,sha256=xFEYhAvNqrOYqwt4jrEYOuYkjJn0zclZhTelW_Q1-rw,2325
|
|
38
39
|
pkgs/filesystem_utils/_s3_session.py,sha256=_jLK5C-UElT5Sl5teM2pFR7fQe11NlhUd04EgwN9FRs,3962
|
|
39
40
|
pkgs/filesystem_utils/_sftp_session.py,sha256=6zoF7YsEUp0GpyFb-BeIhUAWvbTK7IUjvPNJ1B0vEyI,4743
|
|
40
|
-
pkgs/filesystem_utils/file_type_utils.py,sha256=
|
|
41
|
+
pkgs/filesystem_utils/file_type_utils.py,sha256=Au79J66Rh_Yyt_dXctsygPAg1Y4QRawqwRhbvnCpDIY,1933
|
|
41
42
|
pkgs/filesystem_utils/filesystem_session.py,sha256=BQ2Go8Mu9-GcnaWh2Pm4x7ugLVsres6XrOQ8RoiEpcE,1045
|
|
42
43
|
pkgs/serialization/__init__.py,sha256=LifasRW0a50A3qRFmo2bf3FQ6TXhZWOTz2-CVTgPjcQ,753
|
|
43
44
|
pkgs/serialization/missing_sentry.py,sha256=aM_9KxbCk9dVvXvcOKgkIQBqFWvLhv8QlIUCiuFEXMo,806
|
|
44
45
|
pkgs/serialization/opaque_key.py,sha256=FIfXEE0DA1U8R_taFbQ1RCoTSgehrPjP06-qvo-GeNQ,177
|
|
45
|
-
pkgs/serialization/serial_class.py,sha256=
|
|
46
|
+
pkgs/serialization/serial_class.py,sha256=fEceYVIV8dc3Ium-okYxp9cCNFg05HcliXqoCcV5Wcc,6151
|
|
46
47
|
pkgs/serialization/serial_union.py,sha256=xpdeqCrRd0sNCaUwBQRzje6V40ndCbJpZrLX2K0d5xo,2741
|
|
47
48
|
pkgs/serialization/yaml.py,sha256=yoJtu7_ixnJV6uTxA_U1PpK5F_ixT08AKVh5ocyYwXM,1466
|
|
48
49
|
pkgs/serialization_util/__init__.py,sha256=MVKqHTUl2YnWZAFG9xCxu1SgmkQ5xPofrAGlYg6h7rI,330
|
|
@@ -67,7 +68,7 @@ pkgs/type_spec/test.py,sha256=4ueujBq-pEgnX3Z69HyPmD-bullFXmpixcpVzfOkhP4,489
|
|
|
67
68
|
pkgs/type_spec/util.py,sha256=79SLJsSPVnBe2_3CTF6J-7-QD9nRr6o8MKvfjyx53eI,4864
|
|
68
69
|
pkgs/type_spec/actions_registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
70
|
pkgs/type_spec/actions_registry/__main__.py,sha256=JGwKxcAmrQdbpVR2vwknoimN1Q-r5h4SADw1cYLYzgk,4331
|
|
70
|
-
pkgs/type_spec/actions_registry/emit_typescript.py,sha256=
|
|
71
|
+
pkgs/type_spec/actions_registry/emit_typescript.py,sha256=nzqnZbI2OyFSQtyWxINfEsUBF-oLcfUJJnxxeIbzlLE,5990
|
|
71
72
|
pkgs/type_spec/parts/base.py.prepart,sha256=St6P21VP8Um_nagZFuSyNzYiKDSrsK3TNOuGY_1O8cg,2186
|
|
72
73
|
pkgs/type_spec/parts/base.ts.prepart,sha256=2FJJvpg2olCcavxj0nbYWdwKl6KeScour2JjSvN42l8,1001
|
|
73
74
|
pkgs/type_spec/type_info/__main__.py,sha256=pmVjVqXyVh8vKTNCTFgz80Sg74C5BKToP3E6GS-X_So,857
|
|
@@ -99,14 +100,14 @@ uncountable/integration/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
99
100
|
uncountable/integration/db/connect.py,sha256=mE3bdV0huclH2iT_dXCQdRL4LkjIuf_myAR64RTWXEs,498
|
|
100
101
|
uncountable/integration/db/session.py,sha256=96cGQXpe6IugBTdSsjdP0S5yhJ6toSmbVB6qhc3FJzE,693
|
|
101
102
|
uncountable/integration/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
102
|
-
uncountable/integration/executors/executors.py,sha256=
|
|
103
|
+
uncountable/integration/executors/executors.py,sha256=Kzisp1eKufGCWrHIw4mmAj-l1UQ2oJsJR7I-_mksnVs,5441
|
|
103
104
|
uncountable/integration/executors/generic_upload_executor.py,sha256=0vs9NVk1UL2FBhiMCH6o8p4KtVXNFNvv861QCOD3UU4,10375
|
|
104
105
|
uncountable/integration/executors/script_executor.py,sha256=BBQ9f0l7uH2hgKf60jtm-pONzwk-EeOhM2qBAbv_URo,846
|
|
105
106
|
uncountable/integration/queue_runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
106
107
|
uncountable/integration/queue_runner/job_scheduler.py,sha256=lhvcl11jyk_wg9BSc1Xyh5u8iEtsdqQFW83FPLupjQI,6189
|
|
107
108
|
uncountable/integration/queue_runner/queue_runner.py,sha256=0BmYu5zHdothTevGsB-nXg6MBd1UD-WkP3h1WCKMdQg,710
|
|
108
109
|
uncountable/integration/queue_runner/types.py,sha256=8qTq29BTSa5rmW6CBlBntP0pNIiDcwu1wHa78pjroS0,219
|
|
109
|
-
uncountable/integration/queue_runner/worker.py,sha256=
|
|
110
|
+
uncountable/integration/queue_runner/worker.py,sha256=5bcfg_1tVlAenRDCD89gJI3Ru7ev-Sk7Gok5wjLYxnI,4588
|
|
110
111
|
uncountable/integration/queue_runner/command_server/__init__.py,sha256=gQPVILGpWzCr2i5GJyoqna7AOSFvtn4tav69gB78mTQ,571
|
|
111
112
|
uncountable/integration/queue_runner/command_server/command_client.py,sha256=DJb0TUVFkiiLBEQzHSN94sTRnuEbutNEgdN39XmnOXI,2046
|
|
112
113
|
uncountable/integration/queue_runner/command_server/command_server.py,sha256=yyXryhiEC2eGS0yFElLGsVzSKwOuYvj-zp22jQorkv0,2138
|
|
@@ -143,7 +144,7 @@ uncountable/types/client_config_t.py,sha256=6dStfR0IEHiPW8f9_aF3DD_tHmXXw2rEVrgp
|
|
|
143
144
|
uncountable/types/curves.py,sha256=W6uMpG5SyW1MS82szNpxkFEn1MnxNpBFyFbQb2Ysfng,366
|
|
144
145
|
uncountable/types/curves_t.py,sha256=lKhRM-2cZ_sFaW7pa_I_Ipz_pJhm3_yTFehRXI79pKk,1416
|
|
145
146
|
uncountable/types/entity.py,sha256=ECvhswTj9xp4gUEKTZoZYyxHvx1oyvE5FNiGNfSyUgk,528
|
|
146
|
-
uncountable/types/entity_t.py,sha256=
|
|
147
|
+
uncountable/types/entity_t.py,sha256=MPLE0CCrROH89JUE9qyI3MLL0DtVzsuXlJ8bG-x-K4A,16731
|
|
147
148
|
uncountable/types/experiment_groups.py,sha256=_0OXcPzSAbkE-rfKt5tPx178YJ4pcEKZvrCxUHgDnvw,309
|
|
148
149
|
uncountable/types/experiment_groups_t.py,sha256=qEs8YW0eJOJ_sCOObT5v9QRx9wsjLYpJqJhCJXa-vNA,721
|
|
149
150
|
uncountable/types/field_values.py,sha256=uuIWX-xmfvcinYPdfkWJeb56zzQY01mc9rmotMPMh24,503
|
|
@@ -161,7 +162,7 @@ uncountable/types/input_attributes_t.py,sha256=mD9JIagE8TQ0KVwGkl-hinKz_gcunV3y3
|
|
|
161
162
|
uncountable/types/inputs.py,sha256=jFZHyo0ZOGJ3bb4TOPXovhE3Fo1-kf7B7T3usk4Sqg8,467
|
|
162
163
|
uncountable/types/inputs_t.py,sha256=CpuuKRduZGET_wvkGUpUFN6rbZCHsdOIp1veEM-hspI,2143
|
|
163
164
|
uncountable/types/integration_server.py,sha256=61NuGs1pbgovU5Vuje7oN9HpLwOGCCw9Q_CcUvt_0qI,385
|
|
164
|
-
uncountable/types/integration_server_t.py,sha256=
|
|
165
|
+
uncountable/types/integration_server_t.py,sha256=SSNM53lHyHpTjHUkXZowIs2hB7jSsNZyuJsiXldaISE,1234
|
|
165
166
|
uncountable/types/job_definition.py,sha256=DEma_s-0oBo2tPI5u9IU_UDw-9MWbn4mTZsd_RHiYGE,1667
|
|
166
167
|
uncountable/types/job_definition_t.py,sha256=whsat5825Jou1-67dbOkqiibpoavGcsaOhJ3APTUh2Q,7948
|
|
167
168
|
uncountable/types/outputs.py,sha256=sUZx_X-TKCZtLm1YCEH8OISX9DdPlv9ZuUfM3-askCc,281
|
|
@@ -264,7 +265,7 @@ uncountable/types/api/recipes/clear_recipe_outputs.py,sha256=sZ6sWtdfOYp8ORQD9GA
|
|
|
264
265
|
uncountable/types/api/recipes/create_recipe.py,sha256=Gh6Z_7wBfYBMGUgUSixw57ucRjkBhjScIjDg1__4rVU,1570
|
|
265
266
|
uncountable/types/api/recipes/create_recipes.py,sha256=kmTDi0nF5OK5wYIErg_4CY3YsF3pDbrj4LLFqgDNRoU,1940
|
|
266
267
|
uncountable/types/api/recipes/disassociate_recipe_as_input.py,sha256=YcLCle-yQ8A7hPmFg8wPfW4dyJwpMQXNKzJxCEr8xlw,1127
|
|
267
|
-
uncountable/types/api/recipes/edit_recipe_inputs.py,sha256=
|
|
268
|
+
uncountable/types/api/recipes/edit_recipe_inputs.py,sha256=s6PdxXGPDZfsSxa8pEt4KD1f0Lg29Ka1k1r2pSZ0WOg,9949
|
|
268
269
|
uncountable/types/api/recipes/get_column_calculation_values.py,sha256=u_KBiGpXnVLwRruEG-FmopiR8UDkuX6i47B4OY-IYWU,1702
|
|
269
270
|
uncountable/types/api/recipes/get_curve.py,sha256=SPD9kx4m95KPXAD0MawX52IFl8W7gVKj-WmA4Wx2YtU,1126
|
|
270
271
|
uncountable/types/api/recipes/get_recipe_calculations.py,sha256=_sBE5M2xzwagh1beTW32D_HTxqu9OrZTPZBGMba6Myk,1730
|
|
@@ -286,7 +287,7 @@ uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr
|
|
|
286
287
|
uncountable/types/api/triggers/run_trigger.py,sha256=-oZgPyn43xEKSCs81DVNzwaYMCdRJxbM9GY6fsqKwf4,1090
|
|
287
288
|
uncountable/types/api/uploader/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
|
|
288
289
|
uncountable/types/api/uploader/invoke_uploader.py,sha256=6mwVG136oLp9JcbB2I-kZnrcm3aeZzYZB-SFjEImY2o,1314
|
|
289
|
-
UncountablePythonSDK-0.0.
|
|
290
|
-
UncountablePythonSDK-0.0.
|
|
291
|
-
UncountablePythonSDK-0.0.
|
|
292
|
-
UncountablePythonSDK-0.0.
|
|
290
|
+
UncountablePythonSDK-0.0.87.dist-info/METADATA,sha256=DIWpmh2PIjuruJvh55KC8Bu0uj7BmEYU83OvZWDpOQs,2064
|
|
291
|
+
UncountablePythonSDK-0.0.87.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
292
|
+
UncountablePythonSDK-0.0.87.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
|
|
293
|
+
UncountablePythonSDK-0.0.87.dist-info/RECORD,,
|
|
@@ -197,7 +197,7 @@ lint.ignore = [
|
|
|
197
197
|
# ## FROM RUFF UPGRADE
|
|
198
198
|
"PLC2701", # private name imports. should add
|
|
199
199
|
"PLR1702", # too many nested blocks -- add with config. skip
|
|
200
|
-
"
|
|
200
|
+
"C420", # unnecessary dict comprehension. skip
|
|
201
201
|
"SIM113", # index variable should use enumerate. should add.
|
|
202
202
|
"PLR0914" # too many local variables. configure below. should add with config
|
|
203
203
|
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from io import BytesIO
|
|
2
|
+
|
|
3
|
+
from azure.storage.blob import BlobServiceClient, ContainerClient
|
|
4
|
+
|
|
5
|
+
from pkgs.filesystem_utils.file_type_utils import (
|
|
6
|
+
FileObjectData,
|
|
7
|
+
FileSystemBlobConfig,
|
|
8
|
+
FileSystemFileReference,
|
|
9
|
+
FileSystemObject,
|
|
10
|
+
FileTransfer,
|
|
11
|
+
IncompatibleFileReference,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .filesystem_session import FileSystemSession
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _add_slash(prefix: str) -> str:
|
|
18
|
+
if len(prefix) > 0 and prefix[-1] != "/":
|
|
19
|
+
prefix = prefix + "/"
|
|
20
|
+
return prefix
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BlobSession(FileSystemSession):
|
|
24
|
+
config: FileSystemBlobConfig
|
|
25
|
+
|
|
26
|
+
def __init__(self, blob_config: FileSystemBlobConfig) -> None:
|
|
27
|
+
super().__init__()
|
|
28
|
+
self.config = blob_config
|
|
29
|
+
|
|
30
|
+
def start(self) -> None:
|
|
31
|
+
self.service_client: BlobServiceClient | None = BlobServiceClient(
|
|
32
|
+
self.config.account_url, credential=self.config.credential
|
|
33
|
+
)
|
|
34
|
+
self.container_client: ContainerClient | None = (
|
|
35
|
+
self.service_client.get_container_client(self.config.container)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def __enter__(self) -> "BlobSession":
|
|
39
|
+
self.start()
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
|
|
43
|
+
self.service_client = None
|
|
44
|
+
self.container_client = None
|
|
45
|
+
|
|
46
|
+
def list_files(
|
|
47
|
+
self,
|
|
48
|
+
dir_path: FileSystemObject,
|
|
49
|
+
*,
|
|
50
|
+
recursive: bool = False,
|
|
51
|
+
valid_extensions: list[str] | None = None,
|
|
52
|
+
) -> list[FileSystemObject]:
|
|
53
|
+
if not isinstance(dir_path, FileSystemFileReference):
|
|
54
|
+
raise IncompatibleFileReference()
|
|
55
|
+
|
|
56
|
+
assert self.service_client is not None and self.container_client is not None, (
|
|
57
|
+
"call to list_files on uninitialized blob session"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
filesystem_file_references: list[FileSystemObject] = []
|
|
61
|
+
prefix = _add_slash(dir_path.filepath)
|
|
62
|
+
for blob in self.container_client.list_blobs(name_starts_with=prefix):
|
|
63
|
+
if not recursive and (
|
|
64
|
+
blob.name == prefix or "/" in blob.name[len(prefix) :]
|
|
65
|
+
):
|
|
66
|
+
continue
|
|
67
|
+
if valid_extensions is None or any(
|
|
68
|
+
blob.name.endswith(valid_extension)
|
|
69
|
+
for valid_extension in valid_extensions
|
|
70
|
+
):
|
|
71
|
+
filesystem_file_references.append(
|
|
72
|
+
FileSystemFileReference(
|
|
73
|
+
filepath=blob.name,
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return filesystem_file_references
|
|
78
|
+
|
|
79
|
+
def download_files(
|
|
80
|
+
self,
|
|
81
|
+
filepaths: list[FileSystemObject],
|
|
82
|
+
) -> list[FileObjectData]:
|
|
83
|
+
downloaded_files: list[FileObjectData] = []
|
|
84
|
+
assert self.service_client is not None and self.container_client is not None, (
|
|
85
|
+
"call to download_files on uninitialized blob session"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
for file_object in filepaths:
|
|
89
|
+
if (
|
|
90
|
+
not isinstance(file_object, FileSystemFileReference)
|
|
91
|
+
or file_object.filename is None
|
|
92
|
+
):
|
|
93
|
+
raise IncompatibleFileReference()
|
|
94
|
+
|
|
95
|
+
blob_client = self.container_client.get_blob_client(file_object.filepath)
|
|
96
|
+
download_stream = blob_client.download_blob()
|
|
97
|
+
file_data = download_stream.readall()
|
|
98
|
+
downloaded_files.append(
|
|
99
|
+
FileObjectData(
|
|
100
|
+
file_data=file_data,
|
|
101
|
+
file_IO=BytesIO(file_data),
|
|
102
|
+
filename=file_object.filename,
|
|
103
|
+
filepath=file_object.filepath,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return downloaded_files
|
|
108
|
+
|
|
109
|
+
def move_files(self, file_mappings: list[FileTransfer]) -> None:
|
|
110
|
+
assert self.service_client is not None and self.container_client is not None, (
|
|
111
|
+
"call to move_files on uninitialized blob session"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
for src_file, dest_file in file_mappings:
|
|
115
|
+
if not isinstance(src_file, FileSystemFileReference) or not isinstance(
|
|
116
|
+
dest_file, FileSystemFileReference
|
|
117
|
+
):
|
|
118
|
+
raise IncompatibleFileReference()
|
|
119
|
+
|
|
120
|
+
source_blob_client = self.container_client.get_blob_client(
|
|
121
|
+
src_file.filepath
|
|
122
|
+
)
|
|
123
|
+
dest_blob_client = self.container_client.get_blob_client(dest_file.filepath)
|
|
124
|
+
|
|
125
|
+
dest_blob_client.start_copy_from_url(source_blob_client.url)
|
|
126
|
+
source_blob_client.delete_blob()
|
|
127
|
+
|
|
128
|
+
def delete_files(self, filepaths: list[FileSystemObject]) -> None:
|
|
129
|
+
assert self.service_client is not None and self.container_client is not None, (
|
|
130
|
+
"call to delete_files on uninitialized blob session"
|
|
131
|
+
)
|
|
132
|
+
for file_object in filepaths:
|
|
133
|
+
if not isinstance(file_object, FileSystemFileReference):
|
|
134
|
+
raise IncompatibleFileReference()
|
|
135
|
+
|
|
136
|
+
blob_client = self.container_client.get_blob_client(file_object.filepath)
|
|
137
|
+
blob_client.delete_blob()
|
|
@@ -4,6 +4,12 @@ from io import BytesIO
|
|
|
4
4
|
from typing import Optional, Union
|
|
5
5
|
|
|
6
6
|
import paramiko
|
|
7
|
+
from azure.core.credentials import (
|
|
8
|
+
AzureNamedKeyCredential,
|
|
9
|
+
AzureSasCredential,
|
|
10
|
+
TokenCredential,
|
|
11
|
+
)
|
|
12
|
+
from azure.storage.blob import ContainerProperties
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
@dataclass
|
|
@@ -69,3 +75,17 @@ class FileSystemS3Config:
|
|
|
69
75
|
access_key_id: Optional[str]
|
|
70
76
|
secret_access_key: Optional[str]
|
|
71
77
|
session_token: Optional[str]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(kw_only=True)
|
|
81
|
+
class FileSystemBlobConfig:
|
|
82
|
+
account_url: str
|
|
83
|
+
credential: (
|
|
84
|
+
str
|
|
85
|
+
| dict[str, str]
|
|
86
|
+
| AzureNamedKeyCredential
|
|
87
|
+
| AzureSasCredential
|
|
88
|
+
| TokenCredential
|
|
89
|
+
| None
|
|
90
|
+
)
|
|
91
|
+
container: ContainerProperties | str
|
|
@@ -114,8 +114,6 @@ def _get_merged_serial_class_data(type_class: type[Any]) -> _SerialClassData | N
|
|
|
114
114
|
for base in type_class.__bases__:
|
|
115
115
|
curr_base_class_data = _get_merged_serial_class_data(base)
|
|
116
116
|
if curr_base_class_data is not None:
|
|
117
|
-
if base_class_data is None:
|
|
118
|
-
base_class_data = _SerialClassData()
|
|
119
117
|
base_class_data.unconverted_keys |= (
|
|
120
118
|
curr_base_class_data.unconverted_keys
|
|
121
119
|
)
|
|
@@ -111,9 +111,7 @@ def _get_argument_type(arguments: str | None) -> str | None:
|
|
|
111
111
|
argument_parts = arguments.split(".")
|
|
112
112
|
namespace = argument_parts[0]
|
|
113
113
|
type_name = argument_parts[1]
|
|
114
|
-
|
|
115
|
-
return f"{_action_namespace_name(namespace)}.{type_name}"
|
|
116
|
-
return None
|
|
114
|
+
return f"{_action_namespace_name(namespace)}.{type_name}"
|
|
117
115
|
|
|
118
116
|
|
|
119
117
|
def _emit_action_definition(
|
|
@@ -9,6 +9,7 @@ from uncountable.types import (
|
|
|
9
9
|
entity_t,
|
|
10
10
|
field_values_t,
|
|
11
11
|
identifier_t,
|
|
12
|
+
integration_server_t,
|
|
12
13
|
job_definition_t,
|
|
13
14
|
transition_entity_phase_t,
|
|
14
15
|
)
|
|
@@ -33,7 +34,10 @@ def resolve_executor(
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def _create_run_entity(
|
|
36
|
-
*,
|
|
37
|
+
*,
|
|
38
|
+
client: Client,
|
|
39
|
+
logging_settings: job_definition_t.JobLoggingSettings,
|
|
40
|
+
job_uuid: str,
|
|
37
41
|
) -> entity_t.Entity:
|
|
38
42
|
run_entity = client.create_entity(
|
|
39
43
|
entity_type=entity_t.EntityType.ASYNC_JOB,
|
|
@@ -49,6 +53,10 @@ def _create_run_entity(
|
|
|
49
53
|
field_ref_name=async_jobs_t.ASYNC_JOB_STATUS_FIELD_REF_NAME,
|
|
50
54
|
value=async_jobs_t.AsyncJobStatus.IN_PROGRESS,
|
|
51
55
|
),
|
|
56
|
+
field_values_t.FieldRefNameValue(
|
|
57
|
+
field_ref_name=integration_server_t.INTEGRATION_SERVER_RUN_UUID_FIELD_REF_NAME,
|
|
58
|
+
value=job_uuid,
|
|
59
|
+
),
|
|
52
60
|
],
|
|
53
61
|
).entity
|
|
54
62
|
client.transition_entity_phase(
|
|
@@ -80,6 +88,7 @@ def execute_job(
|
|
|
80
88
|
job_definition: job_definition_t.JobDefinition,
|
|
81
89
|
profile_metadata: job_definition_t.ProfileMetadata,
|
|
82
90
|
args: JobArguments,
|
|
91
|
+
job_uuid: str,
|
|
83
92
|
) -> job_definition_t.JobResult:
|
|
84
93
|
with args.logger.push_scope(job_definition.name) as job_logger:
|
|
85
94
|
job = resolve_executor(job_definition.executor, profile_metadata)
|
|
@@ -93,7 +102,9 @@ def execute_job(
|
|
|
93
102
|
and job_definition.logging_settings.enabled
|
|
94
103
|
):
|
|
95
104
|
run_entity = _create_run_entity(
|
|
96
|
-
client=args.client,
|
|
105
|
+
client=args.client,
|
|
106
|
+
logging_settings=job_definition.logging_settings,
|
|
107
|
+
job_uuid=job_uuid,
|
|
97
108
|
)
|
|
98
109
|
result = job.run_outer(args=args)
|
|
99
110
|
except Exception as e:
|
|
@@ -22,6 +22,9 @@ __all__: list[str] = [
|
|
|
22
22
|
"Data",
|
|
23
23
|
"ENDPOINT_METHOD",
|
|
24
24
|
"ENDPOINT_PATH",
|
|
25
|
+
"PlaceholderBase",
|
|
26
|
+
"PlaceholderDisplayMode",
|
|
27
|
+
"PlaceholderType",
|
|
25
28
|
"RecipeInputEdit",
|
|
26
29
|
"RecipeInputEditAddInput",
|
|
27
30
|
"RecipeInputEditAddInstructions",
|
|
@@ -29,11 +32,14 @@ __all__: list[str] = [
|
|
|
29
32
|
"RecipeInputEditChangeBasisViewed",
|
|
30
33
|
"RecipeInputEditClearInputs",
|
|
31
34
|
"RecipeInputEditInputBase",
|
|
35
|
+
"RecipeInputEditPlaceholder",
|
|
32
36
|
"RecipeInputEditSetLot",
|
|
33
37
|
"RecipeInputEditSetRole",
|
|
34
38
|
"RecipeInputEditType",
|
|
35
39
|
"RecipeInputEditUpdateAnnotations",
|
|
36
40
|
"RecipeInputEditUpsertInput",
|
|
41
|
+
"RecipeInputPlaceholder",
|
|
42
|
+
"SubcategoryPlaceholder",
|
|
37
43
|
]
|
|
38
44
|
|
|
39
45
|
ENDPOINT_METHOD = "POST"
|
|
@@ -50,6 +56,7 @@ class RecipeInputEditType(StrEnum):
|
|
|
50
56
|
CHANGE_BASIS = "change_basis"
|
|
51
57
|
ADD_INSTRUCTIONS = "add_instructions"
|
|
52
58
|
SET_ROLE = "set_role"
|
|
59
|
+
SET_PLACEHOLDER = "set_placeholder"
|
|
53
60
|
|
|
54
61
|
|
|
55
62
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -177,9 +184,66 @@ class RecipeInputEditSetRole(RecipeInputEditBase):
|
|
|
177
184
|
ingredient_role_key: typing.Optional[identifier_t.IdentifierKey]
|
|
178
185
|
|
|
179
186
|
|
|
187
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
188
|
+
class PlaceholderType(StrEnum):
|
|
189
|
+
SUBCATEGORY = "subcategory"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
193
|
+
class PlaceholderDisplayMode(StrEnum):
|
|
194
|
+
INLINE = "inline"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
198
|
+
@serial_class(
|
|
199
|
+
named_type_path="sdk.api.recipes.edit_recipe_inputs.PlaceholderBase",
|
|
200
|
+
)
|
|
201
|
+
@dataclasses.dataclass(kw_only=True)
|
|
202
|
+
class PlaceholderBase:
|
|
203
|
+
type: PlaceholderType
|
|
204
|
+
require_for_creation: bool = True
|
|
205
|
+
display_mode: PlaceholderDisplayMode = PlaceholderDisplayMode.INLINE
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
209
|
+
@serial_class(
|
|
210
|
+
named_type_path="sdk.api.recipes.edit_recipe_inputs.SubcategoryPlaceholder",
|
|
211
|
+
parse_require={"type"},
|
|
212
|
+
)
|
|
213
|
+
@dataclasses.dataclass(kw_only=True)
|
|
214
|
+
class SubcategoryPlaceholder(PlaceholderBase):
|
|
215
|
+
type: typing.Literal[PlaceholderType.SUBCATEGORY] = PlaceholderType.SUBCATEGORY
|
|
216
|
+
subcategory_identifier: identifier_t.IdentifierKey
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
220
|
+
RecipeInputPlaceholder = typing.Annotated[
|
|
221
|
+
typing.Union[SubcategoryPlaceholder],
|
|
222
|
+
serial_union_annotation(
|
|
223
|
+
named_type_path="sdk.api.recipes.edit_recipe_inputs.RecipeInputPlaceholder",
|
|
224
|
+
discriminator="type",
|
|
225
|
+
discriminator_map={
|
|
226
|
+
"subcategory": SubcategoryPlaceholder,
|
|
227
|
+
},
|
|
228
|
+
),
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
233
|
+
@serial_class(
|
|
234
|
+
named_type_path="sdk.api.recipes.edit_recipe_inputs.RecipeInputEditPlaceholder",
|
|
235
|
+
parse_require={"type"},
|
|
236
|
+
)
|
|
237
|
+
@dataclasses.dataclass(kw_only=True)
|
|
238
|
+
class RecipeInputEditPlaceholder(RecipeInputEditBase):
|
|
239
|
+
type: typing.Literal[RecipeInputEditType.SET_PLACEHOLDER] = RecipeInputEditType.SET_PLACEHOLDER
|
|
240
|
+
ingredient_key: identifier_t.IdentifierKey
|
|
241
|
+
placeholder: RecipeInputPlaceholder
|
|
242
|
+
|
|
243
|
+
|
|
180
244
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
181
245
|
RecipeInputEdit = typing.Annotated[
|
|
182
|
-
typing.Union[RecipeInputEditClearInputs, RecipeInputEditUpsertInput, RecipeInputEditAddInput, RecipeInputEditUpdateAnnotations, RecipeInputEditSetLot, RecipeInputEditChangeBasisViewed, RecipeInputEditAddInstructions, RecipeInputEditSetRole],
|
|
246
|
+
typing.Union[RecipeInputEditClearInputs, RecipeInputEditUpsertInput, RecipeInputEditAddInput, RecipeInputEditUpdateAnnotations, RecipeInputEditSetLot, RecipeInputEditChangeBasisViewed, RecipeInputEditAddInstructions, RecipeInputEditSetRole, RecipeInputEditPlaceholder],
|
|
183
247
|
serial_union_annotation(
|
|
184
248
|
named_type_path="sdk.api.recipes.edit_recipe_inputs.RecipeInputEdit",
|
|
185
249
|
discriminator="type",
|
|
@@ -192,6 +256,7 @@ RecipeInputEdit = typing.Annotated[
|
|
|
192
256
|
"change_basis": RecipeInputEditChangeBasisViewed,
|
|
193
257
|
"add_instructions": RecipeInputEditAddInstructions,
|
|
194
258
|
"set_role": RecipeInputEditSetRole,
|
|
259
|
+
"set_placeholder": RecipeInputEditPlaceholder,
|
|
195
260
|
},
|
|
196
261
|
),
|
|
197
262
|
]
|
uncountable/types/entity_t.py
CHANGED
|
@@ -60,6 +60,7 @@ __all__: list[str] = [
|
|
|
60
60
|
"equipment_maintenance": "Equipment Maintenance",
|
|
61
61
|
"equipment_type": "Equipment Type",
|
|
62
62
|
"experiment_group": "Experiment Group",
|
|
63
|
+
"experiment_group_member": "Experiment Group Member",
|
|
63
64
|
"experiment_group_type": "Experiment Group Type",
|
|
64
65
|
"favorite_link": "Favorite Link",
|
|
65
66
|
"field_option_set": "Field Option Set",
|
|
@@ -89,6 +90,7 @@ __all__: list[str] = [
|
|
|
89
90
|
"license": "License",
|
|
90
91
|
"maintenance_schedule": "Maintenance Schedule",
|
|
91
92
|
"material_family": "Material Family",
|
|
93
|
+
"measurement_group_recipe": "Measurement Group Recipe",
|
|
92
94
|
"ml_job": "Batch Processing Job",
|
|
93
95
|
"naming_scheme": "Naming Scheme",
|
|
94
96
|
"naming_counter": "Naming Counter",
|
|
@@ -217,6 +219,7 @@ class EntityType(StrEnum):
|
|
|
217
219
|
EQUIPMENT_MAINTENANCE = "equipment_maintenance"
|
|
218
220
|
EQUIPMENT_TYPE = "equipment_type"
|
|
219
221
|
EXPERIMENT_GROUP = "experiment_group"
|
|
222
|
+
EXPERIMENT_GROUP_MEMBER = "experiment_group_member"
|
|
220
223
|
EXPERIMENT_GROUP_TYPE = "experiment_group_type"
|
|
221
224
|
FAVORITE_LINK = "favorite_link"
|
|
222
225
|
FIELD_OPTION_SET = "field_option_set"
|
|
@@ -246,6 +249,7 @@ class EntityType(StrEnum):
|
|
|
246
249
|
LICENSE = "license"
|
|
247
250
|
MAINTENANCE_SCHEDULE = "maintenance_schedule"
|
|
248
251
|
MATERIAL_FAMILY = "material_family"
|
|
252
|
+
MEASUREMENT_GROUP_RECIPE = "measurement_group_recipe"
|
|
249
253
|
ML_JOB = "ml_job"
|
|
250
254
|
NAMING_SCHEME = "naming_scheme"
|
|
251
255
|
NAMING_COUNTER = "naming_counter"
|
|
@@ -15,6 +15,7 @@ from . import client_config_t
|
|
|
15
15
|
|
|
16
16
|
__all__: list[str] = [
|
|
17
17
|
"EnvironmentConfig",
|
|
18
|
+
"INTEGRATION_SERVER_RUN_UUID_FIELD_REF_NAME",
|
|
18
19
|
"IntegrationEnvironment",
|
|
19
20
|
]
|
|
20
21
|
|
|
@@ -34,4 +35,8 @@ class EnvironmentConfig:
|
|
|
34
35
|
auth_retrieval: auth_retrieval_t.AuthRetrieval
|
|
35
36
|
base_url: str
|
|
36
37
|
client_options: typing.Optional[client_config_t.ClientConfigOptions] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
41
|
+
INTEGRATION_SERVER_RUN_UUID_FIELD_REF_NAME = "unc_integration_server_run_uuid"
|
|
37
42
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: UncountablePythonSDK
|
|
3
|
-
Version: 0.0.85
|
|
4
|
-
Summary: Uncountable SDK
|
|
5
|
-
Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
|
|
6
|
-
Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
|
|
7
|
-
Project-URL: Issues, https://github.com/uncountableinc/uncountable-python-sdk/issues
|
|
8
|
-
Keywords: uncountable,sdk,api,uncountable-sdk
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Environment :: Console
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Topic :: Software Development
|
|
15
|
-
Classifier: Topic :: Utilities
|
|
16
|
-
Classifier: Typing :: Typed
|
|
17
|
-
Requires-Python: >=3.11
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
Requires-Dist: aiotus ==1.*
|
|
20
|
-
Requires-Dist: aiohttp ==3.*
|
|
21
|
-
Requires-Dist: requests ==2.*
|
|
22
|
-
Requires-Dist: SQLAlchemy >=1.4.0
|
|
23
|
-
Requires-Dist: APScheduler ==3.*
|
|
24
|
-
Requires-Dist: python-dateutil ==2.*
|
|
25
|
-
Requires-Dist: shelljob ==0.*
|
|
26
|
-
Requires-Dist: PyYAML ==6.*
|
|
27
|
-
Requires-Dist: google-api-python-client ==2.*
|
|
28
|
-
Requires-Dist: tqdm ==4.*
|
|
29
|
-
Requires-Dist: pysftp ==0.*
|
|
30
|
-
Requires-Dist: opentelemetry-api ==1.*
|
|
31
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-common ==1.*
|
|
32
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-http ==1.*
|
|
33
|
-
Requires-Dist: opentelemetry-sdk ==1.*
|
|
34
|
-
Requires-Dist: paramiko ==3.*
|
|
35
|
-
Requires-Dist: boto3 ==1.*
|
|
36
|
-
Requires-Dist: flask ==3.*
|
|
37
|
-
Requires-Dist: simplejson ==3.*
|
|
38
|
-
Requires-Dist: grpcio ==1.67.1
|
|
39
|
-
Requires-Dist: protobuf ==4.*
|
|
40
|
-
Provides-Extra: test
|
|
41
|
-
Requires-Dist: mypy ==1.* ; extra == 'test'
|
|
42
|
-
Requires-Dist: ruff ==0.* ; extra == 'test'
|
|
43
|
-
Requires-Dist: pytest ==8.* ; extra == 'test'
|
|
44
|
-
Requires-Dist: coverage[toml] ==7.* ; extra == 'test'
|
|
45
|
-
Requires-Dist: pytest-cov ==5.* ; extra == 'test'
|
|
46
|
-
Requires-Dist: pytest-xdist ==3.* ; extra == 'test'
|
|
47
|
-
|
|
48
|
-
# Uncountable Python SDK
|
|
49
|
-
|
|
50
|
-
## Documentation
|
|
51
|
-
[https://uncountableinc.github.io/uncountable-python-sdk/](https://uncountableinc.github.io/uncountable-python-sdk/)
|
|
52
|
-
|
|
53
|
-
## Installation
|
|
54
|
-
|
|
55
|
-
Install from PyPI:
|
|
56
|
-
|
|
57
|
-
```console
|
|
58
|
-
pip install UncountablePythonSDK
|
|
59
|
-
```
|
|
60
|
-
|
{UncountablePythonSDK-0.0.85.dist-info → UncountablePythonSDK-0.0.87.dist-info}/top_level.txt
RENAMED
|
File without changes
|