redis-featureform 3.0.0__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.
Files changed (167) hide show
  1. featureform/__init__.py +335 -0
  2. featureform/_apply_context.py +48 -0
  3. featureform/_apply_wait.py +183 -0
  4. featureform/_generated/__init__.py +1 -0
  5. featureform/_generated/openapi/__init__.py +13 -0
  6. featureform/_generated/openapi/_internal.py +1631 -0
  7. featureform/_generated/openapi/github_com_redislabsdev_featureform_internal_rest_types.py +31 -0
  8. featureform/_generated/openapi/server.py +115 -0
  9. featureform/_generated/openapi/types.py +365 -0
  10. featureform/_generated/proto/__init__.py +1 -0
  11. featureform/_generated/proto/featureform/__init__.py +1 -0
  12. featureform/_generated/proto/featureform/v2/__init__.py +1 -0
  13. featureform/_generated/proto/featureform/v2/apply_pb2.py +66 -0
  14. featureform/_generated/proto/featureform/v2/apply_pb2.pyi +463 -0
  15. featureform/_generated/proto/featureform/v2/apply_pb2_grpc.py +147 -0
  16. featureform/_generated/proto/featureform/v2/apply_pb2_grpc.pyi +82 -0
  17. featureform/_generated/proto/featureform/v2/audit_pb2.py +47 -0
  18. featureform/_generated/proto/featureform/v2/audit_pb2.pyi +140 -0
  19. featureform/_generated/proto/featureform/v2/audit_pb2_grpc.py +97 -0
  20. featureform/_generated/proto/featureform/v2/audit_pb2_grpc.pyi +49 -0
  21. featureform/_generated/proto/featureform/v2/catalog_pb2.py +60 -0
  22. featureform/_generated/proto/featureform/v2/catalog_pb2.pyi +292 -0
  23. featureform/_generated/proto/featureform/v2/catalog_pb2_grpc.py +192 -0
  24. featureform/_generated/proto/featureform/v2/catalog_pb2_grpc.pyi +88 -0
  25. featureform/_generated/proto/featureform/v2/common_pb2.py +44 -0
  26. featureform/_generated/proto/featureform/v2/common_pb2.pyi +128 -0
  27. featureform/_generated/proto/featureform/v2/common_pb2_grpc.py +24 -0
  28. featureform/_generated/proto/featureform/v2/common_pb2_grpc.pyi +20 -0
  29. featureform/_generated/proto/featureform/v2/dataframe_pb2.py +67 -0
  30. featureform/_generated/proto/featureform/v2/dataframe_pb2.pyi +342 -0
  31. featureform/_generated/proto/featureform/v2/dataframe_pb2_grpc.py +97 -0
  32. featureform/_generated/proto/featureform/v2/dataframe_pb2_grpc.pyi +49 -0
  33. featureform/_generated/proto/featureform/v2/provider_pb2.py +261 -0
  34. featureform/_generated/proto/featureform/v2/provider_pb2.pyi +3013 -0
  35. featureform/_generated/proto/featureform/v2/provider_pb2_grpc.py +277 -0
  36. featureform/_generated/proto/featureform/v2/provider_pb2_grpc.pyi +106 -0
  37. featureform/_generated/proto/featureform/v2/rbac_pb2.py +157 -0
  38. featureform/_generated/proto/featureform/v2/rbac_pb2.pyi +1281 -0
  39. featureform/_generated/proto/featureform/v2/rbac_pb2_grpc.py +1072 -0
  40. featureform/_generated/proto/featureform/v2/rbac_pb2_grpc.pyi +260 -0
  41. featureform/_generated/proto/featureform/v2/resource_pb2.py +127 -0
  42. featureform/_generated/proto/featureform/v2/resource_pb2.pyi +1529 -0
  43. featureform/_generated/proto/featureform/v2/resource_pb2_grpc.py +24 -0
  44. featureform/_generated/proto/featureform/v2/resource_pb2_grpc.pyi +20 -0
  45. featureform/_generated/proto/featureform/v2/resource_service_pb2.py +215 -0
  46. featureform/_generated/proto/featureform/v2/resource_service_pb2.pyi +2295 -0
  47. featureform/_generated/proto/featureform/v2/resource_service_pb2_grpc.py +1645 -0
  48. featureform/_generated/proto/featureform/v2/resource_service_pb2_grpc.pyi +373 -0
  49. featureform/_generated/proto/featureform/v2/secret_provider_pb2.py +83 -0
  50. featureform/_generated/proto/featureform/v2/secret_provider_pb2.pyi +607 -0
  51. featureform/_generated/proto/featureform/v2/secret_provider_pb2_grpc.py +277 -0
  52. featureform/_generated/proto/featureform/v2/secret_provider_pb2_grpc.pyi +106 -0
  53. featureform/_generated/proto/featureform/v2/serving_pb2.py +112 -0
  54. featureform/_generated/proto/featureform/v2/serving_pb2.pyi +783 -0
  55. featureform/_generated/proto/featureform/v2/serving_pb2_grpc.py +146 -0
  56. featureform/_generated/proto/featureform/v2/serving_pb2_grpc.pyi +76 -0
  57. featureform/_generated/proto/featureform/v2/version_pb2.py +45 -0
  58. featureform/_generated/proto/featureform/v2/version_pb2.pyi +127 -0
  59. featureform/_generated/proto/featureform/v2/version_pb2_grpc.py +146 -0
  60. featureform/_generated/proto/featureform/v2/version_pb2_grpc.pyi +76 -0
  61. featureform/_generated/proto/featureform/v2/workspace_pb2.py +65 -0
  62. featureform/_generated/proto/featureform/v2/workspace_pb2.pyi +296 -0
  63. featureform/_generated/proto/featureform/v2/workspace_pb2_grpc.py +322 -0
  64. featureform/_generated/proto/featureform/v2/workspace_pb2_grpc.pyi +124 -0
  65. featureform/_generated/proto/scheduler/__init__.py +1 -0
  66. featureform/_generated/proto/scheduler/v1/__init__.py +1 -0
  67. featureform/_generated/proto/scheduler/v1/scheduler_pb2.py +180 -0
  68. featureform/_generated/proto/scheduler/v1/scheduler_pb2.pyi +1994 -0
  69. featureform/_generated/proto/scheduler/v1/scheduler_pb2_grpc.py +1145 -0
  70. featureform/_generated/proto/scheduler/v1/scheduler_pb2_grpc.pyi +301 -0
  71. featureform/_internal/__init__.py +1 -0
  72. featureform/_internal/http.py +231 -0
  73. featureform/_internal/logging.py +299 -0
  74. featureform/_internal/secret_refs.py +118 -0
  75. featureform/_internal/telemetry/__init__.py +73 -0
  76. featureform/_internal/telemetry/base.py +126 -0
  77. featureform/_internal/telemetry/config.py +71 -0
  78. featureform/_internal/telemetry/noop.py +89 -0
  79. featureform/_internal/telemetry/otel.py +152 -0
  80. featureform/_version.py +39 -0
  81. featureform/auth/__init__.py +21 -0
  82. featureform/auth/runtime.py +125 -0
  83. featureform/auth/token.py +559 -0
  84. featureform/cli/__init__.py +23 -0
  85. featureform/cli/admin_client.py +888 -0
  86. featureform/cli/auth/__init__.py +113 -0
  87. featureform/cli/auth/clock.py +22 -0
  88. featureform/cli/auth/discovery.py +240 -0
  89. featureform/cli/auth/flows.py +130 -0
  90. featureform/cli/auth/fs.py +14 -0
  91. featureform/cli/auth/profiles.py +153 -0
  92. featureform/cli/auth/service.py +575 -0
  93. featureform/cli/auth/session_store.py +311 -0
  94. featureform/cli/auth/types.py +80 -0
  95. featureform/cli/commands/__init__.py +27 -0
  96. featureform/cli/commands/_common.py +23 -0
  97. featureform/cli/commands/_provider_config.py +1173 -0
  98. featureform/cli/commands/apply.py +887 -0
  99. featureform/cli/commands/audit.py +58 -0
  100. featureform/cli/commands/auth.py +437 -0
  101. featureform/cli/commands/catalog.py +116 -0
  102. featureform/cli/commands/config.py +188 -0
  103. featureform/cli/commands/dataframe.py +182 -0
  104. featureform/cli/commands/graph.py +534 -0
  105. featureform/cli/commands/machine_credential.py +142 -0
  106. featureform/cli/commands/provider.py +1222 -0
  107. featureform/cli/commands/rbac.py +445 -0
  108. featureform/cli/commands/scheduler.py +1101 -0
  109. featureform/cli/commands/secret_provider.py +405 -0
  110. featureform/cli/commands/workspace.py +167 -0
  111. featureform/cli/env.py +69 -0
  112. featureform/cli/errors.py +155 -0
  113. featureform/cli/main.py +525 -0
  114. featureform/cli/output/__init__.py +13 -0
  115. featureform/cli/output/base.py +49 -0
  116. featureform/cli/output/json_fmt.py +71 -0
  117. featureform/cli/output/table.py +163 -0
  118. featureform/cli/output/yaml_fmt.py +64 -0
  119. featureform/cli/scheduler_client.py +1281 -0
  120. featureform/cli/utils/__init__.py +5 -0
  121. featureform/cli/utils/context.py +252 -0
  122. featureform/client.py +7961 -0
  123. featureform/config.py +156 -0
  124. featureform/dataframe/__init__.py +12 -0
  125. featureform/dataframe/client.py +787 -0
  126. featureform/dataframe/result.py +288 -0
  127. featureform/dataframe/stream.py +146 -0
  128. featureform/errors.py +867 -0
  129. featureform/py.typed +0 -0
  130. featureform/resources/__init__.py +123 -0
  131. featureform/resources/base.py +256 -0
  132. featureform/resources/dataset.py +319 -0
  133. featureform/resources/entity.py +47 -0
  134. featureform/resources/feature.py +399 -0
  135. featureform/resources/label.py +250 -0
  136. featureform/resources/provider.py +729 -0
  137. featureform/resources/realtime_feature.py +621 -0
  138. featureform/resources/training.py +333 -0
  139. featureform/serving/__init__.py +12 -0
  140. featureform/serving/_decode.py +93 -0
  141. featureform/serving/_metadata.py +99 -0
  142. featureform/serving/_realtime.py +253 -0
  143. featureform/serving/cache.py +190 -0
  144. featureform/serving/client.py +788 -0
  145. featureform/serving/types.py +134 -0
  146. featureform/spark.py +171 -0
  147. featureform/spark_env.py +31 -0
  148. featureform/transport/__init__.py +49 -0
  149. featureform/transport/base.py +42 -0
  150. featureform/transport/grpc.py +760 -0
  151. featureform/transport/rest.py +224 -0
  152. featureform/types/__init__.py +226 -0
  153. featureform/types/_base.py +24 -0
  154. featureform/types/catalog.py +62 -0
  155. featureform/types/entity.py +39 -0
  156. featureform/types/graph.py +487 -0
  157. featureform/types/patch.py +14 -0
  158. featureform/types/provider.py +1098 -0
  159. featureform/types/resource.py +407 -0
  160. featureform/types/secret.py +232 -0
  161. featureform/types/secret_provider.py +206 -0
  162. featureform/types/workspace.py +54 -0
  163. featureform/version.py +228 -0
  164. redis_featureform-3.0.0.dist-info/METADATA +448 -0
  165. redis_featureform-3.0.0.dist-info/RECORD +167 -0
  166. redis_featureform-3.0.0.dist-info/WHEEL +4 -0
  167. redis_featureform-3.0.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,335 @@
1
+ """Featureform Python client library.
2
+
3
+ Enterprise-grade Python client for Featureform v2 API.
4
+ """
5
+
6
+ from featureform._apply_context import get_active_apply_client, get_active_apply_workspace_id
7
+ from featureform._apply_wait import ApplyWaitError, WaitTarget
8
+ from featureform._internal.logging import (
9
+ LogLevel,
10
+ configure_logging,
11
+ current_request_id,
12
+ get_logger,
13
+ request_id_context,
14
+ temporary_log_level,
15
+ )
16
+ from featureform._internal.telemetry import TelemetryConfig
17
+ from featureform._version import resolve_version
18
+ from featureform.client import (
19
+ ApplyResult,
20
+ ApplyWaitResult,
21
+ Client,
22
+ GraphClientBase,
23
+ GRPCGraphClient,
24
+ PingResult,
25
+ PlannedJob,
26
+ PlannedTask,
27
+ ProviderClient,
28
+ ResourceChange,
29
+ RESTGraphClient,
30
+ SecretProviderClient,
31
+ WorkspaceClient,
32
+ )
33
+ from featureform.config import Config
34
+ from featureform.errors import (
35
+ AlreadyExistsError,
36
+ ConnectionError,
37
+ DataframeFallbackError,
38
+ DataframePlanResolutionError,
39
+ FeatureformError,
40
+ FlightFallbackUnavailableError,
41
+ InternalError,
42
+ InvalidArgumentError,
43
+ NotFoundError,
44
+ ProviderWorkspaceMismatchError,
45
+ SparkExecutionError,
46
+ TimeoutError,
47
+ ValidationError,
48
+ )
49
+
50
+ # New DSL resource classes
51
+ from featureform.resources import (
52
+ AggregateFeature,
53
+ AttributeFeature,
54
+ Dataset,
55
+ EnvSecretProvider,
56
+ Feature,
57
+ FeatureBuilder,
58
+ FeatureInput,
59
+ FeatureResource,
60
+ FeatureView,
61
+ FunctionNormalizerDetails,
62
+ IcebergPrimaryDataset,
63
+ IcebergProvider,
64
+ K8sSecretProvider,
65
+ Label,
66
+ LabelBuilder,
67
+ LabelResource,
68
+ PostgresPrimaryDataset,
69
+ PostgresProvider,
70
+ PostgresTransformedDataset,
71
+ PrimaryDataset,
72
+ RealtimeFeature,
73
+ RealtimeInput,
74
+ RedisClusterProvider,
75
+ RedisProvider,
76
+ Resource,
77
+ ResourceRegistry,
78
+ S3Provider,
79
+ SecretProviderBase,
80
+ SerializerDetails,
81
+ SnowflakePrimaryDataset,
82
+ SnowflakeProvider,
83
+ SnowflakeTransformedDataset,
84
+ SparkProvider,
85
+ SparkTransformedDataset,
86
+ TrainingSet,
87
+ TransformedDataset,
88
+ VaultSecretProvider,
89
+ postgres_sql_transformation,
90
+ realtime_feature,
91
+ spark_sql_transformation,
92
+ )
93
+ from featureform.resources import Provider as ProviderBase
94
+ from featureform.resources.entity import Entity
95
+ from featureform.spark import DataframeExecutionInfo, SparkOptions, get_execution_info
96
+ from featureform.types import (
97
+ CLEAR,
98
+ AWSSecretRef,
99
+ AWSSecretsManagerSecretConfig,
100
+ AzureSecretRef,
101
+ CreateWorkspaceRequest,
102
+ DynamoDBConfig,
103
+ EnvSecretConfig,
104
+ EnvSecretRef,
105
+ GCPSecretRef,
106
+ K8sSecretRef,
107
+ KafkaConfig,
108
+ KubernetesSecretConfig,
109
+ PostgresConfig,
110
+ Provider,
111
+ ProviderConfig,
112
+ ProviderType,
113
+ RedisConfig,
114
+ RegisterProviderRequest,
115
+ RegisterSecretProviderRequest,
116
+ S3Config,
117
+ SecretProvider,
118
+ SecretProviderConfig,
119
+ SecretProviderType,
120
+ SecretRef,
121
+ SnowflakeConfig,
122
+ SparkKerberosConfig,
123
+ SparkProviderConfig,
124
+ UpdateProviderRequest,
125
+ UpdateSecretProviderRequest,
126
+ UpdateWorkspaceRequest,
127
+ VaultSecretConfig,
128
+ VaultSecretRef,
129
+ Workspace,
130
+ )
131
+
132
+ # Type helpers from types.resource
133
+ from featureform.types.resource import (
134
+ AggregateFunction,
135
+ FeatureLag,
136
+ IcebergConfig,
137
+ IcebergTableLocation,
138
+ MaterializationEngine,
139
+ PostgresDatasetConfig,
140
+ ScalarType,
141
+ SparkEnv,
142
+ SparkExecutionDefaults,
143
+ SparkJobConfig,
144
+ TableLocation,
145
+ TrainingSetType,
146
+ ValueType,
147
+ )
148
+
149
+ __version__ = resolve_version()
150
+
151
+
152
+ def get_postgres(name: str) -> PostgresProvider:
153
+ """Resolve a registered Postgres provider within an active apply context.
154
+
155
+ Use this in resource files loaded by ``ff apply`` when you want method-style
156
+ authoring such as ``postgres.dataset(...)`` or ``@postgres.sql_transformation(...)``.
157
+ Outside apply, use ``client.get_postgres(...)`` instead.
158
+ """
159
+
160
+ client = get_active_apply_client()
161
+ workspace_id = get_active_apply_workspace_id()
162
+ if client is None:
163
+ raise InvalidArgumentError(
164
+ "ff.get_postgres() requires an active apply context. Use client.get_postgres() "
165
+ "outside resource-file apply flows."
166
+ )
167
+ return client.get_postgres(name, workspace=workspace_id)
168
+
169
+
170
+ def get_provider(name: str) -> ProviderBase:
171
+ """Resolve a registered provider within an active apply context.
172
+
173
+ Outside apply, use ``client.get_provider(...)`` instead.
174
+ """
175
+
176
+ client = get_active_apply_client()
177
+ workspace_id = get_active_apply_workspace_id()
178
+ if client is None:
179
+ raise InvalidArgumentError(
180
+ "ff.get_provider() requires an active apply context. Use client.get_provider() "
181
+ "outside resource-file apply flows."
182
+ )
183
+ return client.get_provider(name, workspace=workspace_id)
184
+
185
+
186
+ __all__ = [
187
+ # Version
188
+ "__version__",
189
+ # Client
190
+ "ApplyResult",
191
+ "ApplyWaitError",
192
+ "ApplyWaitResult",
193
+ "Client",
194
+ "GRPCGraphClient",
195
+ "GraphClientBase",
196
+ "PingResult",
197
+ "PlannedJob",
198
+ "PlannedTask",
199
+ "ProviderClient",
200
+ "RESTGraphClient",
201
+ "ResourceChange",
202
+ "SecretProviderClient",
203
+ "WaitTarget",
204
+ "WorkspaceClient",
205
+ # Config
206
+ "Config",
207
+ "CLEAR",
208
+ # Logging
209
+ "LogLevel",
210
+ "configure_logging",
211
+ "current_request_id",
212
+ "get_logger",
213
+ "request_id_context",
214
+ "temporary_log_level",
215
+ # Telemetry
216
+ "TelemetryConfig",
217
+ # Errors
218
+ "AlreadyExistsError",
219
+ "ConnectionError",
220
+ "DataframeFallbackError",
221
+ "DataframePlanResolutionError",
222
+ "FeatureformError",
223
+ "FlightFallbackUnavailableError",
224
+ "get_postgres",
225
+ "get_provider",
226
+ "InternalError",
227
+ "InvalidArgumentError",
228
+ "NotFoundError",
229
+ "ProviderWorkspaceMismatchError",
230
+ "SparkExecutionError",
231
+ "TimeoutError",
232
+ "ValidationError",
233
+ # New DSL - Base
234
+ "Resource",
235
+ "ResourceRegistry",
236
+ # New DSL - Secrets
237
+ "SecretProviderBase",
238
+ "EnvSecretProvider",
239
+ "VaultSecretProvider",
240
+ "K8sSecretProvider",
241
+ "EnvSecretRef",
242
+ "AWSSecretRef",
243
+ "GCPSecretRef",
244
+ "AzureSecretRef",
245
+ "VaultSecretRef",
246
+ "K8sSecretRef",
247
+ "SecretRef",
248
+ # New DSL - Providers (NOT Resources - registered separately)
249
+ "IcebergProvider",
250
+ "PostgresProvider",
251
+ "SnowflakeProvider",
252
+ "ProviderBase",
253
+ "RedisProvider",
254
+ "RedisClusterProvider",
255
+ "S3Provider",
256
+ "SparkProvider",
257
+ "SparkOptions",
258
+ "get_execution_info",
259
+ "postgres_sql_transformation",
260
+ "spark_sql_transformation",
261
+ # New DSL - Entity
262
+ "Entity",
263
+ # New DSL - Datasets
264
+ "Dataset",
265
+ "IcebergPrimaryDataset",
266
+ "PostgresPrimaryDataset",
267
+ "PostgresTransformedDataset",
268
+ "PrimaryDataset",
269
+ "SnowflakePrimaryDataset",
270
+ "SnowflakeTransformedDataset",
271
+ "SparkTransformedDataset",
272
+ "TransformedDataset",
273
+ # New DSL - Features (builder pattern)
274
+ "Feature",
275
+ "FeatureBuilder",
276
+ "FeatureResource",
277
+ "FeatureInput",
278
+ "AttributeFeature",
279
+ "AggregateFeature",
280
+ "FunctionNormalizerDetails",
281
+ # New DSL - Labels (builder pattern)
282
+ "Label",
283
+ "LabelBuilder",
284
+ "LabelResource",
285
+ "RealtimeFeature",
286
+ "RealtimeInput",
287
+ "SerializerDetails",
288
+ "realtime_feature",
289
+ # New DSL - Training
290
+ "FeatureView",
291
+ "TrainingSet",
292
+ # Type helpers
293
+ "AggregateFunction",
294
+ "FeatureLag",
295
+ "IcebergConfig",
296
+ "IcebergTableLocation",
297
+ "MaterializationEngine",
298
+ "PostgresDatasetConfig",
299
+ "ScalarType",
300
+ "SparkEnv",
301
+ "SparkExecutionDefaults",
302
+ "SparkJobConfig",
303
+ "DataframeExecutionInfo",
304
+ "TableLocation",
305
+ "TrainingSetType",
306
+ "ValueType",
307
+ # Workspace Types
308
+ "CreateWorkspaceRequest",
309
+ "UpdateWorkspaceRequest",
310
+ "Workspace",
311
+ # Provider Types (legacy)
312
+ "KafkaConfig",
313
+ "PostgresConfig",
314
+ "Provider",
315
+ "ProviderConfig",
316
+ "ProviderType",
317
+ "DynamoDBConfig",
318
+ "RedisConfig",
319
+ "RegisterProviderRequest",
320
+ "S3Config",
321
+ "SparkKerberosConfig",
322
+ "SparkProviderConfig",
323
+ "SnowflakeConfig",
324
+ "UpdateProviderRequest",
325
+ # SecretProvider Types
326
+ "AWSSecretsManagerSecretConfig",
327
+ "EnvSecretConfig",
328
+ "KubernetesSecretConfig",
329
+ "RegisterSecretProviderRequest",
330
+ "SecretProvider",
331
+ "SecretProviderConfig",
332
+ "SecretProviderType",
333
+ "UpdateSecretProviderRequest",
334
+ "VaultSecretConfig",
335
+ ]
@@ -0,0 +1,48 @@
1
+ """Apply-time provider resolution context for resource files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from contextlib import contextmanager
7
+ from contextvars import ContextVar
8
+ from typing import TYPE_CHECKING
9
+ from uuid import UUID
10
+
11
+ if TYPE_CHECKING:
12
+ from featureform.client import Client
13
+
14
+
15
+ _active_client: ContextVar[Client | None] = ContextVar(
16
+ "featureform_active_apply_client",
17
+ default=None,
18
+ )
19
+ _active_workspace_id: ContextVar[UUID | None] = ContextVar(
20
+ "featureform_active_apply_workspace_id",
21
+ default=None,
22
+ )
23
+
24
+
25
+ @contextmanager
26
+ def apply_resolution_context(
27
+ *,
28
+ client: Client | None,
29
+ workspace_id: UUID | None,
30
+ ) -> Iterator[None]:
31
+ """Temporarily expose the active apply client/workspace to resource files."""
32
+ client_token = _active_client.set(client)
33
+ workspace_token = _active_workspace_id.set(workspace_id)
34
+ try:
35
+ yield
36
+ finally:
37
+ _active_client.reset(client_token)
38
+ _active_workspace_id.reset(workspace_token)
39
+
40
+
41
+ def get_active_apply_client() -> Client | None:
42
+ """Return the active apply client, if any."""
43
+ return _active_client.get()
44
+
45
+
46
+ def get_active_apply_workspace_id() -> UUID | None:
47
+ """Return the active apply workspace ID, if any."""
48
+ return _active_workspace_id.get()
@@ -0,0 +1,183 @@
1
+ """Shared apply wait helpers for CLI and Python client flows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from collections.abc import Callable, Mapping
7
+ from enum import Enum
8
+ from typing import Any, cast
9
+
10
+
11
+ class WaitTarget(str, Enum):
12
+ """Wait target options for apply jobs."""
13
+
14
+ RUNNING = "running"
15
+ FINISHED = "finished"
16
+
17
+
18
+ TERMINAL_JOB_STATUSES = {"COMPLETED", "COMPLETED_WITH_WARNINGS", "FAILED", "CANCELED"}
19
+ SUCCESSFUL_TERMINAL_JOB_STATUSES = {"COMPLETED", "COMPLETED_WITH_WARNINGS"}
20
+ FAILED_JOB_STATUSES = {"FAILED", "CANCELED"}
21
+
22
+
23
+ class ApplyWaitError(Exception):
24
+ """Raised when apply wait semantics fail or time out."""
25
+
26
+
27
+ class ApplyWaitTimeoutError(ApplyWaitError):
28
+ """Raised when apply waiting exceeds the allotted timeout."""
29
+
30
+
31
+ class ApplyJobVisibilityTimeoutError(ApplyWaitTimeoutError):
32
+ """Timed out before the scheduler job became visible."""
33
+
34
+ def __init__(self, job_id: str) -> None:
35
+ super().__init__(f"Timed out waiting for job '{job_id}' to become visible.")
36
+ self.job_id = job_id
37
+
38
+
39
+ class ApplyJobStateTimeoutError(ApplyWaitTimeoutError):
40
+ """Timed out before the scheduler job reached the requested wait target."""
41
+
42
+ def __init__(self, job_id: str, wait_for: WaitTarget, last_status: str) -> None:
43
+ super().__init__(
44
+ f"Timed out waiting for job '{job_id}' to reach {wait_for.value}. "
45
+ f"Last status: {last_status}."
46
+ )
47
+ self.job_id = job_id
48
+ self.wait_for = wait_for
49
+ self.last_status = last_status
50
+
51
+
52
+ class ApplyJobFailedError(ApplyWaitError):
53
+ """Apply job reached a failed terminal state."""
54
+
55
+ def __init__(
56
+ self,
57
+ job_id: str,
58
+ status: str,
59
+ *,
60
+ detail: str | None = None,
61
+ job: dict[str, Any] | None = None,
62
+ ) -> None:
63
+ message = f"Apply job '{job_id}' finished with status {status}."
64
+ if detail:
65
+ message = f"{message} {detail}"
66
+ super().__init__(message)
67
+ self.job_id = job_id
68
+ self.status = status
69
+ self.detail = detail
70
+ self.job = job
71
+
72
+
73
+ class ApplyJobFetchError(ApplyWaitError):
74
+ """Fetching scheduler job state failed."""
75
+
76
+ def __init__(self, job_id: str, detail: str | None = None) -> None:
77
+ message = f"Failed to fetch apply job '{job_id}'."
78
+ if detail:
79
+ message = f"{message} {detail}"
80
+ super().__init__(message)
81
+ self.job_id = job_id
82
+ self.detail = detail
83
+
84
+
85
+ class ApplyWorkspaceVersionTimeoutError(ApplyWaitTimeoutError):
86
+ """Timed out waiting for the applied workspace version to become visible."""
87
+
88
+ def __init__(self, workspace_id: object, minimum: int, last_seen: int) -> None:
89
+ super().__init__(
90
+ f"Timed out waiting for workspace '{workspace_id}' to reach applied version "
91
+ f"{minimum}. Last seen version: {last_seen}."
92
+ )
93
+ self.workspace_id = workspace_id
94
+ self.minimum = minimum
95
+ self.last_seen = last_seen
96
+
97
+
98
+ def summarize_failed_job(job: Mapping[str, object]) -> str | None:
99
+ """Build a concise failure summary from scheduler job data."""
100
+ parts: list[str] = []
101
+
102
+ job_error_value: object = job.get("error_message")
103
+ job_error = str(job_error_value or "").strip()
104
+ if job_error:
105
+ parts.append(f"job error: {job_error}")
106
+
107
+ tasks_value: object = job.get("tasks")
108
+ if isinstance(tasks_value, list):
109
+ failed_task = next(
110
+ (
111
+ task
112
+ for task in tasks_value
113
+ if isinstance(task, Mapping)
114
+ and str(cast(Mapping[str, object], task).get("status", "")).upper() == "FAILED"
115
+ ),
116
+ None,
117
+ )
118
+ if isinstance(failed_task, Mapping):
119
+ task = cast(Mapping[str, object], failed_task)
120
+ task_def_id_value: object = task.get("task_def_id")
121
+ task_id_value: object = task.get("id")
122
+ task_error_value: object = task.get("error_message")
123
+ task_label = str(task_def_id_value or task_id_value or "").strip()
124
+ task_error = str(task_error_value or "").strip()
125
+ if task_label:
126
+ summary = f"failed task: {task_label}"
127
+ if task_error:
128
+ summary = f"{summary}; error: {task_error}"
129
+ parts.append(summary)
130
+
131
+ if not parts:
132
+ return None
133
+ return " ".join(parts)
134
+
135
+
136
+ def wait_target_reached(status: str, wait_for: WaitTarget) -> bool:
137
+ """Return whether the current job status satisfies the wait target."""
138
+ if wait_for == WaitTarget.RUNNING:
139
+ return status == "RUNNING" or status in SUCCESSFUL_TERMINAL_JOB_STATUSES
140
+ return status in TERMINAL_JOB_STATUSES
141
+
142
+
143
+ def wait_for_job(
144
+ *,
145
+ get_job: Callable[[str], dict[str, Any]],
146
+ job_id: str,
147
+ wait_for: WaitTarget,
148
+ timeout_seconds: float,
149
+ poll_interval_seconds: float,
150
+ is_not_found_error: Callable[[Exception], bool] | None = None,
151
+ monotonic_fn: Callable[[], float] = time.monotonic,
152
+ sleep_fn: Callable[[float], None] = time.sleep,
153
+ ) -> dict[str, Any]:
154
+ """Poll the scheduler until a job reaches the requested state."""
155
+ deadline = monotonic_fn() + timeout_seconds
156
+
157
+ while True:
158
+ try:
159
+ job = get_job(job_id)
160
+ except Exception as exc:
161
+ if is_not_found_error is not None and is_not_found_error(exc):
162
+ if monotonic_fn() >= deadline:
163
+ raise ApplyJobVisibilityTimeoutError(job_id) from None
164
+ sleep_fn(poll_interval_seconds)
165
+ continue
166
+ raise
167
+
168
+ raw_status = job.get("status")
169
+ status = str(raw_status).upper().strip() if raw_status not in (None, "") else "UNKNOWN"
170
+ if status in FAILED_JOB_STATUSES:
171
+ raise ApplyJobFailedError(
172
+ job_id,
173
+ status,
174
+ detail=summarize_failed_job(job),
175
+ job=job,
176
+ )
177
+ if wait_target_reached(status, wait_for):
178
+ return job
179
+
180
+ if monotonic_fn() >= deadline:
181
+ raise ApplyJobStateTimeoutError(job_id, wait_for, status)
182
+
183
+ sleep_fn(poll_interval_seconds)
@@ -0,0 +1 @@
1
+ """Generated code from protobuf and OpenAPI."""
@@ -0,0 +1,13 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: swagger.json
3
+ # timestamp: 2026-04-19T23:23:13+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import RootModel
10
+
11
+
12
+ class Model(RootModel[Any]):
13
+ root: Any