UncountablePythonSDK 0.0.43__py3-none-any.whl → 0.0.45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.43
3
+ Version: 0.0.45
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
@@ -28,8 +28,8 @@ pkgs/argument_parser/case_convert.py,sha256=NuJLJUJRbyVb6_Slen4uqaStEHbcOS1d-hBB
28
28
  pkgs/filesystem_utils/__init__.py,sha256=yxfwtYFvqq_fjMl-tg2jwa6eNPNZUF4ykqsSzALyNdw,1143
29
29
  pkgs/filesystem_utils/_gdrive_session.py,sha256=OZudNoP2HikolnpurVJhJdh5fgzqbaZQvn53ReGGXx4,11015
30
30
  pkgs/filesystem_utils/_local_session.py,sha256=xFEYhAvNqrOYqwt4jrEYOuYkjJn0zclZhTelW_Q1-rw,2325
31
- pkgs/filesystem_utils/_sftp_session.py,sha256=jtvxmMs3H1RneuJhztx1pCSbX9hBq3srFV1IVNqUlBA,4729
32
- pkgs/filesystem_utils/file_type_utils.py,sha256=F6P2chbXIDwAUAiMc_ja1_rxrhGy3fA1-ymAnIK3gVU,1259
31
+ pkgs/filesystem_utils/_sftp_session.py,sha256=gNoUD_b4MuVqWj31nU-FpfpXZlyWkwdEHtX1S8W6gpQ,4727
32
+ pkgs/filesystem_utils/file_type_utils.py,sha256=CC2c7127TE84vlRTy7bpE4Qvy7ytxuRNA3dwP91PXA8,1257
33
33
  pkgs/filesystem_utils/filesystem_session.py,sha256=BQ2Go8Mu9-GcnaWh2Pm4x7ugLVsres6XrOQ8RoiEpcE,1045
34
34
  pkgs/serialization/__init__.py,sha256=LifasRW0a50A3qRFmo2bf3FQ6TXhZWOTz2-CVTgPjcQ,753
35
35
  pkgs/serialization/missing_sentry.py,sha256=aM_9KxbCk9dVvXvcOKgkIQBqFWvLhv8QlIUCiuFEXMo,806
@@ -50,7 +50,7 @@ pkgs/type_spec/config.py,sha256=IQyo2Vj11uNt7_d6jQxvominAOU-oPB8ldEmuGzJLpU,4644
50
50
  pkgs/type_spec/emit_io_ts.py,sha256=Ghd8XYqyNYldHQDepwa9GLfHXcoi48ztBw84K28ETic,5707
51
51
  pkgs/type_spec/emit_open_api.py,sha256=92POd3j4nrrROrw9M-bgEmK4ukYbI0TW6E9IigJzoTE,24512
52
52
  pkgs/type_spec/emit_open_api_util.py,sha256=y2slouAflUJmyTPH_d4CbXql9zpOoD1uTOELqL3NX-M,2448
53
- pkgs/type_spec/emit_python.py,sha256=R5CZcApOdTzbA4R5klFOxXt4AQtTI72GTxp4Non8q2E,46509
53
+ pkgs/type_spec/emit_python.py,sha256=bQUqVmfVgAXw8rGcqlg93YbUsUvawgR2o_NrV_c9Zio,46873
54
54
  pkgs/type_spec/emit_typescript.py,sha256=cdr5h8N70PuwORcvhURUujzwH9r1LVwJB8V2EoipGkw,17917
55
55
  pkgs/type_spec/emit_typescript_util.py,sha256=sR7ys3Ilnh6SQiXJbfYk4pxfOu0bDjbUFTEYEW-ud6c,863
56
56
  pkgs/type_spec/load_types.py,sha256=BOLyndtxPqqhUqZAh-lIbN5IZBaW_m-bdYpKGsbPyXM,3654
@@ -72,21 +72,21 @@ pkgs/type_spec/value_spec/types.py,sha256=a2zxbbCRWepY1l8OtjeCDKgBKFPFHVgV99oP6p
72
72
  uncountable/__init__.py,sha256=8l8XWNCKsu7TG94c-xa2KHpDegvxDC2FyQISdWC763Y,89
73
73
  uncountable/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  uncountable/core/__init__.py,sha256=RFv0kO6rKFf1PtBPu83hCGmxqkJamRtsgQ9_-ztw7tA,341
75
- uncountable/core/async_batch.py,sha256=Zo02TICZ7AK81Qa02NQ8fp8uaijQph1FEta28K3cKb0,749
75
+ uncountable/core/async_batch.py,sha256=QTEnazSEO8GtMvI9qnSXIcgWxk3dbAVg2HijoXW5ddQ,1173
76
76
  uncountable/core/client.py,sha256=50vR11GwwyTc3qOIahUxCJK77zCM7LOXwbZdHoZCZgY,10168
77
77
  uncountable/core/file_upload.py,sha256=TkQ0fKbbYrPgns1Jh51JU35DUqZHB3ljOaVgjSlBx9Y,3149
78
78
  uncountable/core/types.py,sha256=s2CjqYJpsmbC7xMwxxT7kJ_V9bwokrjjWVVjpMcQpKI,333
79
79
  uncountable/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
80
  uncountable/integration/construct_client.py,sha256=y8x65wzG-_84uLlnSVQG4WuMhyVGUfdJVmhy8gs8v4A,1082
81
- uncountable/integration/cron.py,sha256=czW-RY2T5lD7yJwyVZcFg9240mXURrzeExd1EL6zHeg,1179
81
+ uncountable/integration/cron.py,sha256=n4HjW-KNruYQ_09iwNBd9i4-Juy_9ddzbf71_wnydHA,1674
82
82
  uncountable/integration/entrypoint.py,sha256=Hs-XUTTxjrxsb31lytN7yVViiBYc10x96W_Vy6iq95A,1382
83
- uncountable/integration/job.py,sha256=ZaVm77v2Pkhyy0FA8PJPp4hd7yNshXs1T3NlZjNi03U,1398
83
+ uncountable/integration/job.py,sha256=5HJcjtwj_pGFYAZ6SK7xa9RqDXCoBb5rAb_OHdTR61Q,1463
84
84
  uncountable/integration/server.py,sha256=OlRVxrpnpJ_QXJ27kQt4z_U5HnI0pfcm5OkxhN_Sa-Y,3347
85
85
  uncountable/integration/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
86
  uncountable/integration/db/connect.py,sha256=YtQHJ1DBGPhxKFRCfiXqohOYUceKSxMVOJ88aPI48Ug,181
87
87
  uncountable/integration/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
88
  uncountable/integration/executors/executors.py,sha256=v5ClGVUlvrZcMdmGQa8Ll668G_HGTnKpGOnTM7UMZCQ,956
89
- uncountable/integration/executors/generic_upload_executor.py,sha256=lCHGIMCe3t_UvEgHMBoOneWR2zbYaGXztt5F2zDRBLw,8796
89
+ uncountable/integration/executors/generic_upload_executor.py,sha256=axGpIfF0IMHilGzFawDizWoDhYffeqYXuhfyYkQhWF4,8754
90
90
  uncountable/integration/executors/script_executor.py,sha256=OmSBOtU48G3mqza9c2lCm84pGGyaDk-ZBJCx3RsdJXc,846
91
91
  uncountable/integration/secret_retrieval/__init__.py,sha256=3QXVj35w8rRMxVvmmsViFYDi3lcb3g70incfalOEm6o,87
92
92
  uncountable/integration/secret_retrieval/retrieve_secret.py,sha256=M0qXVJpD8hMYIFypHFeyh598sqmIDX8ZOyXK23CluF0,1323
@@ -121,8 +121,8 @@ uncountable/types/input_attributes.py,sha256=IrIKQnHqHdS1Utdfzr9GnOe17a8riaqYcO1
121
121
  uncountable/types/input_attributes_t.py,sha256=wE1ekiQfb72Z9VpF5SHipKJkgaJFUHJrNkkJdmuhF9w,820
122
122
  uncountable/types/inputs.py,sha256=6RIEFfCxLqpeHEGOpu63O4i8zPogjGeB7wiV_rPBw_g,404
123
123
  uncountable/types/inputs_t.py,sha256=RW7gF9zTOwByu-nMTMVuBabLOuWKx4O1nvfgvx_R55o,1611
124
- uncountable/types/job_definition.py,sha256=ImmUw8y4_nEsz_pBEceZdbnJDhFZRfm9BtW4hHhe9ik,1584
125
- uncountable/types/job_definition_t.py,sha256=oqF988ub1UZDRdMrg2cndTMn7oml102LQQKZ-vLUAVI,5406
124
+ uncountable/types/job_definition.py,sha256=W2wth9vPRBVL0temwnSmvfxxPMKFxZqbQtsMXn8BU3U,1637
125
+ uncountable/types/job_definition_t.py,sha256=wz1DE0L9BMGKwTVCvOo8DSv1QZlvwkNpiEnivSo_o3A,5552
126
126
  uncountable/types/outputs.py,sha256=sUZx_X-TKCZtLm1YCEH8OISX9DdPlv9ZuUfM3-askCc,281
127
127
  uncountable/types/outputs_t.py,sha256=2aORUOr0ls1ZYo-ddkWax3D1ZndmQsWtHfJxpYozlhg,656
128
128
  uncountable/types/permissions.py,sha256=1mRnSsmRgjuLgp6pylTwwACD_YRIcmlqxHkufwZtMns,297
@@ -136,7 +136,7 @@ uncountable/types/recipe_identifiers_t.py,sha256=suumLdp_8SZD7oqIxSEaV9D1irMeLJ5
136
136
  uncountable/types/recipe_inputs.py,sha256=5ThNFEOGbADqqfj1V3hNvZUcO5pn1Gm17LmzQkWDrtI,351
137
137
  uncountable/types/recipe_inputs_t.py,sha256=t5nFzuF1AU6SUHpya6xKHSlcckmt3XxAuk9ofjZ2oGU,746
138
138
  uncountable/types/recipe_links.py,sha256=YRiu6t7FWr7ZWycIaOPXBtQFqbY_q5unaP6KQzh1LzE,343
139
- uncountable/types/recipe_links_t.py,sha256=RPS-Q4FAvKlio7pwufIdERQhlNr27IZGVxMPaoTVqrI,1400
139
+ uncountable/types/recipe_links_t.py,sha256=ZaTeP8AsIT9-gDRi_uQreDsAzEmqF34YTesh9uydYa0,1417
140
140
  uncountable/types/recipe_metadata.py,sha256=Zpcrupq_mlT2UhXpMJup5XjtubmvgZ8SbJNq7da2pCs,441
141
141
  uncountable/types/recipe_metadata_t.py,sha256=y2O2rIanS6WG0j1UDSsRSwyEKYNDeNaixlR3PmkYnkU,1455
142
142
  uncountable/types/recipe_output_metadata.py,sha256=83jKZCvogG9QjZeJpQptdSam_MnnUj-tqh9EVIrTWjE,322
@@ -235,7 +235,7 @@ uncountable/types/api/triggers/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr
235
235
  uncountable/types/api/triggers/run_trigger.py,sha256=_Rpha9nxXI3Xr17CrGDtofg4HZ81x2lt0rMZ6As0qfE,893
236
236
  uncountable/types/api/uploader/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
237
237
  uncountable/types/api/uploader/invoke_uploader.py,sha256=2RCvnccwEm6J5dt7UJVLUKxfk49vfpGXjBLCWIBD3Ck,1006
238
- UncountablePythonSDK-0.0.43.dist-info/METADATA,sha256=vhbJVX4f-c2phTYrI3H2MJ4NCXfkOHVeHa3NHq-SEGk,1707
239
- UncountablePythonSDK-0.0.43.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
240
- UncountablePythonSDK-0.0.43.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
241
- UncountablePythonSDK-0.0.43.dist-info/RECORD,,
238
+ UncountablePythonSDK-0.0.45.dist-info/METADATA,sha256=WAhZRRAEVBAG_NLepXUkHuR71qY-2uRlIWNrIsN7JJY,1707
239
+ UncountablePythonSDK-0.0.45.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
240
+ UncountablePythonSDK-0.0.45.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
241
+ UncountablePythonSDK-0.0.45.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.1.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -63,7 +63,7 @@ class SFTPSession(FileSystemSession):
63
63
  super().__init__()
64
64
  self.host: str = sftp_config.ip
65
65
  self.username: str = sftp_config.username
66
- self.key_file: str | paramiko.AgentKey | None = (
66
+ self.key_file: str | paramiko.RSAKey | None = (
67
67
  sftp_config.pem_path
68
68
  if sftp_config.pem_path is not None
69
69
  else sftp_config.pem_key
@@ -55,7 +55,7 @@ class FileSystemSFTPConfig:
55
55
  ip: str
56
56
  username: str
57
57
  pem_path: str | None
58
- pem_key: paramiko.AgentKey | None = None
58
+ pem_key: paramiko.RSAKey | None = None
59
59
  password: str | None = None
60
60
  valid_extensions: Optional[tuple[str]] = None
61
61
  recursive: bool = True
@@ -719,6 +719,14 @@ def _emit_properties(
719
719
  default = "None"
720
720
  elif prop.has_default:
721
721
  default = _emit_value(ctx, prop.spec_type, prop.default)
722
+ if (
723
+ isinstance(prop.spec_type, builder.SpecTypeInstance)
724
+ and (
725
+ prop.spec_type.defn_type.is_base_type(builder.BaseTypeName.s_list)
726
+ )
727
+ and default == "[]"
728
+ ):
729
+ default = "dataclasses.field(default_factory=list)"
722
730
  class_out.write(f"{INDENT * num_indent}{py_name}: {ref_type}")
723
731
  if default:
724
732
  class_out.write(f" = {default}")
@@ -4,9 +4,14 @@ from uncountable.types.async_batch import AsyncBatchRequest
4
4
  from uncountable.types.async_batch_processor import AsyncBatchProcessorBase
5
5
 
6
6
 
7
+ class AsyncBatchSubmissionError(BaseException):
8
+ pass
9
+
10
+
7
11
  class AsyncBatchProcessor(AsyncBatchProcessorBase):
8
12
  _client: Client
9
13
  _queue: list[AsyncBatchRequest]
14
+ _submitted_job_ids: list[base_t.ObjectId]
10
15
 
11
16
  def __init__(self, *, client: Client) -> None:
12
17
  super().__init__()
@@ -16,7 +21,16 @@ class AsyncBatchProcessor(AsyncBatchProcessorBase):
16
21
  def _enqueue(self, req: async_batch_t.AsyncBatchRequest) -> None:
17
22
  self._queue.append(req)
18
23
 
24
+ def current_queue_size(self) -> int:
25
+ return len(self._queue)
26
+
19
27
  def send(self) -> base_t.ObjectId:
28
+ if len(self._queue) == 0:
29
+ raise AsyncBatchSubmissionError("queue is empty")
20
30
  job_id = self._client.execute_batch_load_async(requests=self._queue).job_id
31
+ self._submitted_job_ids.append(job_id)
21
32
  self._queue = []
22
33
  return job_id
34
+
35
+ def get_submitted_job_ids(self) -> list[base_t.ObjectId]:
36
+ return self._submitted_job_ids
@@ -1,6 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
 
3
3
  from pkgs.argument_parser import CachedParser
4
+ from uncountable.core.async_batch import AsyncBatchProcessor
4
5
  from uncountable.integration.construct_client import construct_uncountable_client
5
6
  from uncountable.integration.executors.executors import resolve_executor
6
7
  from uncountable.integration.job import CronJobArguments, JobLogger
@@ -18,9 +19,12 @@ cron_args_parser = CachedParser(CronJobArgs)
18
19
 
19
20
  def cron_job_executor(**kwargs: dict) -> None:
20
21
  args_passed = cron_args_parser.parse_storage(kwargs)
22
+ client = construct_uncountable_client(profile_meta=args_passed.profile_metadata)
23
+ batch_processor = AsyncBatchProcessor(client=client)
21
24
  args = CronJobArguments(
22
25
  job_definition=args_passed.definition,
23
- client=construct_uncountable_client(profile_meta=args_passed.profile_metadata),
26
+ client=client,
27
+ batch_processor=batch_processor,
24
28
  profile_metadata=args_passed.profile_metadata,
25
29
  logger=JobLogger(
26
30
  profile_metadata=args_passed.profile_metadata,
@@ -33,3 +37,11 @@ def cron_job_executor(**kwargs: dict) -> None:
33
37
  print(f"running job {args_passed.definition.name}")
34
38
 
35
39
  job.run(args=args)
40
+
41
+ if batch_processor.current_queue_size() != 0:
42
+ batch_processor.send()
43
+
44
+ print(f"completed job {args_passed.definition.name}")
45
+ submitted_batch_job_ids = batch_processor.get_submitted_job_ids()
46
+ if len(submitted_batch_job_ids) != 0:
47
+ print("submitted batch jobs", submitted_batch_job_ids)
@@ -2,7 +2,8 @@ import io
2
2
  import os
3
3
  import re
4
4
  from datetime import datetime, timezone
5
- from tempfile import NamedTemporaryFile
5
+
6
+ import paramiko
6
7
 
7
8
  from pkgs.filesystem_utils import (
8
9
  FileObjectData,
@@ -15,7 +16,7 @@ from pkgs.filesystem_utils import (
15
16
  from pkgs.filesystem_utils.filesystem_session import FileSystemSession
16
17
  from uncountable.core.async_batch import AsyncBatchProcessor
17
18
  from uncountable.core.file_upload import DataFileUpload, FileUpload
18
- from uncountable.integration.job import Job, JobArguments, JobLogger, JobResult
19
+ from uncountable.integration.job import Job, JobArguments, JobLogger
19
20
  from uncountable.integration.secret_retrieval import retrieve_secret
20
21
  from uncountable.types.generic_upload_t import (
21
22
  GenericRemoteDirectoryScope,
@@ -24,6 +25,7 @@ from uncountable.types.generic_upload_t import (
24
25
  from uncountable.types.job_definition_t import (
25
26
  GenericUploadDataSource,
26
27
  GenericUploadDataSourceSFTP,
28
+ JobResult,
27
29
  )
28
30
 
29
31
 
@@ -176,14 +178,14 @@ class GenericUploadJob(Job):
176
178
  pem_secret = retrieve_secret(
177
179
  self.data_source.pem_secret, profile_metadata=args.profile_metadata
178
180
  )
179
- with NamedTemporaryFile() as pem_file:
180
- pem_file.write(pem_secret.encode())
181
- sftp_config = FileSystemSFTPConfig(
182
- ip=self.data_source.host,
183
- username=self.data_source.username,
184
- pem_path=pem_file.name,
185
- )
186
- return SFTPSession(sftp_config=sftp_config)
181
+ pem_key = paramiko.RSAKey.from_private_key(io.StringIO(pem_secret))
182
+ sftp_config = FileSystemSFTPConfig(
183
+ ip=self.data_source.host,
184
+ username=self.data_source.username,
185
+ pem_path=None,
186
+ pem_key=pem_key,
187
+ )
188
+ return SFTPSession(sftp_config=sftp_config)
187
189
 
188
190
  def run(self, args: JobArguments) -> JobResult:
189
191
  client = args.client
@@ -1,8 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from dataclasses import dataclass
3
3
 
4
+ from uncountable.core.async_batch import AsyncBatchProcessor
4
5
  from uncountable.core.client import Client
5
- from uncountable.types.job_definition_t import JobDefinition, ProfileMetadata
6
+ from uncountable.types.job_definition_t import JobDefinition, JobResult, ProfileMetadata
6
7
 
7
8
 
8
9
  class JobLogger:
@@ -25,6 +26,7 @@ class JobArgumentsBase:
25
26
  job_definition: JobDefinition
26
27
  profile_metadata: ProfileMetadata
27
28
  client: Client
29
+ batch_processor: AsyncBatchProcessor
28
30
  logger: JobLogger
29
31
 
30
32
 
@@ -37,11 +39,6 @@ class CronJobArguments(JobArgumentsBase):
37
39
  JobArguments = CronJobArguments
38
40
 
39
41
 
40
- @dataclass
41
- class JobResult:
42
- success: bool
43
-
44
-
45
42
  class Job(ABC):
46
43
  _unc_job_registered: bool = False
47
44
 
@@ -23,4 +23,5 @@ from .job_definition_t import AuthRetrievalEnv as AuthRetrievalEnv
23
23
  from .job_definition_t import AuthRetrieval as AuthRetrieval
24
24
  from .job_definition_t import ProfileDefinition as ProfileDefinition
25
25
  from .job_definition_t import ProfileMetadata as ProfileMetadata
26
+ from .job_definition_t import JobResult as JobResult
26
27
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -32,6 +32,7 @@ __all__: list[str] = [
32
32
  "JobExecutorGenericUpload",
33
33
  "JobExecutorScript",
34
34
  "JobExecutorType",
35
+ "JobResult",
35
36
  "ProfileDefinition",
36
37
  "ProfileMetadata",
37
38
  ]
@@ -200,4 +201,10 @@ class ProfileMetadata:
200
201
  name: str
201
202
  base_url: str
202
203
  auth_retrieval: AuthRetrieval
204
+
205
+
206
+ # DO NOT MODIFY -- This file is generated by type_spec
207
+ @dataclasses.dataclass(kw_only=True)
208
+ class JobResult:
209
+ success: bool
203
210
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -46,5 +46,5 @@ class RecipeLink:
46
46
  recipe_id_from: base_t.ObjectId
47
47
  recipe_id_to: base_t.ObjectId
48
48
  link_type: str
49
- name: str
49
+ name: typing.Optional[str]
50
50
  # DO NOT MODIFY -- This file is generated by type_spec