ob-metaflow-extensions 1.1.171rc1__py2.py3-none-any.whl → 1.4.35__py2.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 ob-metaflow-extensions might be problematic. Click here for more details.

Files changed (64) hide show
  1. metaflow_extensions/outerbounds/plugins/__init__.py +6 -3
  2. metaflow_extensions/outerbounds/plugins/apps/app_cli.py +0 -29
  3. metaflow_extensions/outerbounds/plugins/apps/app_deploy_decorator.py +146 -0
  4. metaflow_extensions/outerbounds/plugins/apps/core/__init__.py +10 -0
  5. metaflow_extensions/outerbounds/plugins/apps/core/_state_machine.py +506 -0
  6. metaflow_extensions/outerbounds/plugins/apps/core/_vendor/__init__.py +0 -0
  7. metaflow_extensions/outerbounds/plugins/apps/core/_vendor/spinner/__init__.py +4 -0
  8. metaflow_extensions/outerbounds/plugins/apps/core/_vendor/spinner/spinners.py +478 -0
  9. metaflow_extensions/outerbounds/plugins/apps/core/app_cli.py +1200 -0
  10. metaflow_extensions/outerbounds/plugins/apps/core/app_config.py +146 -0
  11. metaflow_extensions/outerbounds/plugins/apps/core/artifacts.py +0 -0
  12. metaflow_extensions/outerbounds/plugins/apps/core/capsule.py +958 -0
  13. metaflow_extensions/outerbounds/plugins/apps/core/click_importer.py +24 -0
  14. metaflow_extensions/outerbounds/plugins/apps/core/code_package/__init__.py +3 -0
  15. metaflow_extensions/outerbounds/plugins/apps/core/code_package/code_packager.py +618 -0
  16. metaflow_extensions/outerbounds/plugins/apps/core/code_package/examples.py +125 -0
  17. metaflow_extensions/outerbounds/plugins/apps/core/config/__init__.py +12 -0
  18. metaflow_extensions/outerbounds/plugins/apps/core/config/cli_generator.py +161 -0
  19. metaflow_extensions/outerbounds/plugins/apps/core/config/config_utils.py +868 -0
  20. metaflow_extensions/outerbounds/plugins/apps/core/config/schema_export.py +288 -0
  21. metaflow_extensions/outerbounds/plugins/apps/core/config/typed_configs.py +139 -0
  22. metaflow_extensions/outerbounds/plugins/apps/core/config/typed_init_generator.py +398 -0
  23. metaflow_extensions/outerbounds/plugins/apps/core/config/unified_config.py +1088 -0
  24. metaflow_extensions/outerbounds/plugins/apps/core/config_schema.yaml +337 -0
  25. metaflow_extensions/outerbounds/plugins/apps/core/dependencies.py +115 -0
  26. metaflow_extensions/outerbounds/plugins/apps/core/deployer.py +303 -0
  27. metaflow_extensions/outerbounds/plugins/apps/core/experimental/__init__.py +89 -0
  28. metaflow_extensions/outerbounds/plugins/apps/core/perimeters.py +87 -0
  29. metaflow_extensions/outerbounds/plugins/apps/core/secrets.py +164 -0
  30. metaflow_extensions/outerbounds/plugins/apps/core/utils.py +233 -0
  31. metaflow_extensions/outerbounds/plugins/apps/core/validations.py +17 -0
  32. metaflow_extensions/outerbounds/plugins/aws/assume_role_decorator.py +25 -12
  33. metaflow_extensions/outerbounds/plugins/checkpoint_datastores/coreweave.py +9 -77
  34. metaflow_extensions/outerbounds/plugins/checkpoint_datastores/external_chckpt.py +85 -0
  35. metaflow_extensions/outerbounds/plugins/checkpoint_datastores/nebius.py +7 -78
  36. metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py +6 -2
  37. metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py +1 -0
  38. metaflow_extensions/outerbounds/plugins/nvct/nvct_decorator.py +8 -8
  39. metaflow_extensions/outerbounds/plugins/optuna/__init__.py +48 -0
  40. metaflow_extensions/outerbounds/plugins/profilers/simple_card_decorator.py +96 -0
  41. metaflow_extensions/outerbounds/plugins/s3_proxy/__init__.py +7 -0
  42. metaflow_extensions/outerbounds/plugins/s3_proxy/binary_caller.py +132 -0
  43. metaflow_extensions/outerbounds/plugins/s3_proxy/constants.py +11 -0
  44. metaflow_extensions/outerbounds/plugins/s3_proxy/exceptions.py +13 -0
  45. metaflow_extensions/outerbounds/plugins/s3_proxy/proxy_bootstrap.py +59 -0
  46. metaflow_extensions/outerbounds/plugins/s3_proxy/s3_proxy_api.py +93 -0
  47. metaflow_extensions/outerbounds/plugins/s3_proxy/s3_proxy_decorator.py +250 -0
  48. metaflow_extensions/outerbounds/plugins/s3_proxy/s3_proxy_manager.py +225 -0
  49. metaflow_extensions/outerbounds/plugins/snowpark/snowpark_client.py +6 -3
  50. metaflow_extensions/outerbounds/plugins/snowpark/snowpark_decorator.py +13 -7
  51. metaflow_extensions/outerbounds/plugins/snowpark/snowpark_job.py +8 -2
  52. metaflow_extensions/outerbounds/plugins/torchtune/__init__.py +4 -0
  53. metaflow_extensions/outerbounds/plugins/vllm/__init__.py +173 -95
  54. metaflow_extensions/outerbounds/plugins/vllm/status_card.py +9 -9
  55. metaflow_extensions/outerbounds/plugins/vllm/vllm_manager.py +159 -9
  56. metaflow_extensions/outerbounds/remote_config.py +8 -3
  57. metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py +62 -1
  58. metaflow_extensions/outerbounds/toplevel/ob_internal.py +2 -0
  59. metaflow_extensions/outerbounds/toplevel/plugins/optuna/__init__.py +1 -0
  60. metaflow_extensions/outerbounds/toplevel/s3_proxy.py +88 -0
  61. {ob_metaflow_extensions-1.1.171rc1.dist-info → ob_metaflow_extensions-1.4.35.dist-info}/METADATA +2 -2
  62. {ob_metaflow_extensions-1.1.171rc1.dist-info → ob_metaflow_extensions-1.4.35.dist-info}/RECORD +64 -22
  63. {ob_metaflow_extensions-1.1.171rc1.dist-info → ob_metaflow_extensions-1.4.35.dist-info}/WHEEL +0 -0
  64. {ob_metaflow_extensions-1.1.171rc1.dist-info → ob_metaflow_extensions-1.4.35.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,398 @@
1
+ """
2
+ Typed Init Generator for ConfigMeta Classes
3
+
4
+ This module provides a mechanism to dynamically generate explicit typed classes
5
+ from ConfigMeta classes that IDEs can understand and provide autocomplete for.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional, Union, Type, Set
9
+
10
+ from .config_utils import ConfigMeta
11
+
12
+ import os
13
+
14
+ current_dir = os.path.dirname(__file__)
15
+
16
+ TYPED_DICT_IMPORT = """
17
+ import sys
18
+ from typing import TYPE_CHECKING
19
+
20
+ # on 3.8+ use the stdlib TypedDict;
21
+ # in TYPE_CHECKING blocks mypy/pyright still pick it up on older Pythons
22
+ if sys.version_info >= (3, 8):
23
+ from typing import TypedDict
24
+ else:
25
+ if TYPE_CHECKING:
26
+ # for the benefit of type-checkers
27
+ from typing import TypedDict # noqa: F401
28
+ # runtime no-op TypedDict shim
29
+ class _TypedDictMeta(type):
30
+ def __new__(cls, name, bases, namespace, total=True):
31
+ # ignore total at runtime
32
+ return super().__new__(cls, name, bases, namespace)
33
+
34
+ class TypedDict(dict, metaclass=_TypedDictMeta):
35
+ # Runtime stand-in for typing.TypedDict on <3.8.
36
+ pass
37
+ """
38
+
39
+
40
+ def collect_nested_configs_recursive(
41
+ config_class: Type, visited: Optional[Set[str]] = None
42
+ ) -> Dict[str, Type]:
43
+ """
44
+ Recursively collect all nested ConfigMeta classes from a config class.
45
+
46
+ Args:
47
+ config_class: A class that inherits from ConfigMeta
48
+ visited: Set of already visited class names to avoid infinite recursion
49
+
50
+ Returns:
51
+ Dictionary mapping class names to ConfigMeta classes
52
+ """
53
+ if visited is None:
54
+ visited = set()
55
+
56
+ nested_configs = {}
57
+
58
+ # Avoid infinite recursion by tracking visited classes
59
+ if config_class.__name__ in visited:
60
+ return nested_configs
61
+
62
+ visited.add(config_class.__name__)
63
+
64
+ # First pass: collect immediate nested configs
65
+ for field_name, field_info in config_class._fields.items():
66
+ if ConfigMeta.is_instance(field_info.field_type):
67
+ nested_class = field_info.field_type
68
+ nested_configs[nested_class.__name__] = nested_class
69
+
70
+ # Recursively collect nested configs from this nested class
71
+ deeper_nested = collect_nested_configs_recursive(
72
+ nested_class, visited.copy()
73
+ )
74
+ nested_configs.update(deeper_nested)
75
+
76
+ return nested_configs
77
+
78
+
79
+ def generate_typed_class_code(config_class: Type) -> str:
80
+ """
81
+ Generate the actual Python code for a typed class that IDEs can understand.
82
+
83
+ Args:
84
+ config_class: A class that inherits from ConfigMeta
85
+
86
+ Returns:
87
+ Python code string for the typed class
88
+ """
89
+ if not hasattr(config_class, "_fields"):
90
+ raise ValueError(f"Class {config_class.__name__} is not a ConfigMeta class")
91
+
92
+ class_name = f"Typed{config_class.__name__}"
93
+
94
+ # Generate TypedDict for nested configs - now recursive
95
+ nested_typeddict_code = []
96
+
97
+ # Recursively collect all nested configs
98
+ nested_configs = collect_nested_configs_recursive(config_class)
99
+
100
+ # Generate TypedDict classes for all nested configs
101
+ for nested_name, nested_class in nested_configs.items():
102
+ dict_name = f"{nested_name}Dict"
103
+ fields = []
104
+
105
+ for field_name, field_info in nested_class._fields.items():
106
+ field_type = _get_type_string(field_info.field_type, quote_config_meta=True)
107
+ if not field_info.required:
108
+ field_type = f"Optional[{field_type}]"
109
+ fields.append(f" {field_name}: {field_type}")
110
+
111
+ typeddict_code = f"""class {dict_name}(TypedDict, total=False):
112
+ {chr(10).join(fields)}"""
113
+ nested_typeddict_code.append(typeddict_code)
114
+
115
+ # Generate __init__ method signature
116
+ required_params = []
117
+ optional_params = []
118
+ all_assignments = []
119
+
120
+ for field_name, field_info in config_class._fields.items():
121
+ field_type = field_info.field_type
122
+
123
+ # Handle nested ConfigMeta classes
124
+ if ConfigMeta.is_instance(field_type):
125
+ type_hint = f"Optional[{field_type.__name__}Dict]"
126
+ param_line = f" {field_name}: {type_hint} = None"
127
+ optional_params.append(param_line)
128
+ else:
129
+ # All params will be set as options here even if the are required in the
130
+ # configMeta
131
+ type_hint = _get_type_string(field_type)
132
+ param_line = f" {field_name}: Optional[{type_hint}] = None"
133
+ optional_params.append(param_line)
134
+
135
+ all_assignments.append(f' "{field_name}": {field_name}')
136
+
137
+ # Combine required params first, then optional params
138
+ all_params = required_params + optional_params
139
+
140
+ # Generate the class code
141
+ newline = "\n"
142
+ comma_newline = ",\n"
143
+
144
+ # Add **kwargs to the parameter list
145
+ if all_params:
146
+ params_with_kwargs = all_params + [" **kwargs"]
147
+ else:
148
+ params_with_kwargs = [" **kwargs"]
149
+
150
+ class_code = f"""class {class_name}:
151
+ def __init__(
152
+ self,
153
+ {comma_newline.join(params_with_kwargs)}
154
+ ) -> None:
155
+ self._kwargs = {{
156
+ {comma_newline.join(all_assignments)}
157
+ }}
158
+ # Add any additional kwargs
159
+ self._kwargs.update(kwargs)
160
+ # Remove None values
161
+ self._kwargs = {{k: v for k, v in self._kwargs.items() if v is not None}}
162
+ self._config_class = {config_class.__name__}
163
+ self._config = self.create_config()
164
+
165
+ def create_config(self) -> {config_class.__name__}:
166
+ return {config_class.__name__}.from_dict(self._kwargs)
167
+
168
+ def to_dict(self) -> Dict[str, Any]:
169
+ return self._config.to_dict()"""
170
+
171
+ # Combine all code
172
+ full_code = []
173
+ if nested_typeddict_code:
174
+ full_code.extend(nested_typeddict_code)
175
+ full_code.append("") # Empty line
176
+ full_code.append(class_code)
177
+
178
+ return (newline + newline).join(full_code)
179
+
180
+
181
+ def _get_type_string(field_type: Type, quote_config_meta: bool = False) -> str:
182
+ """Convert a type to its string representation for code generation.
183
+
184
+ Args:
185
+ field_type: The type to convert
186
+ quote_config_meta: Whether to quote ConfigMeta type references for forward declarations
187
+ """
188
+ if field_type == str:
189
+ return "str"
190
+ elif field_type == int:
191
+ return "int"
192
+ elif field_type == float:
193
+ return "float"
194
+ elif field_type == bool:
195
+ return "bool"
196
+ elif ConfigMeta.is_instance(field_type):
197
+ # Handle ConfigMeta classes by referencing their Dict type
198
+ dict_type = f"{field_type.__name__}Dict"
199
+ return f'"{dict_type}"' if quote_config_meta else dict_type
200
+ elif hasattr(field_type, "__origin__"):
201
+ # Handle generic types like List[str], Dict[str, str], etc.
202
+ origin = field_type.__origin__
203
+ args = getattr(field_type, "__args__", ())
204
+
205
+ if origin == list:
206
+ if args:
207
+ return f"List[{_get_type_string(args[0], quote_config_meta)}]"
208
+ return "List[Any]"
209
+ elif origin == dict:
210
+ if len(args) == 2:
211
+ return f"Dict[{_get_type_string(args[0], quote_config_meta)}, {_get_type_string(args[1], quote_config_meta)}]"
212
+ return "Dict[str, Any]"
213
+ elif origin == Union:
214
+ # Handle Optional types
215
+ if len(args) == 2 and type(None) in args:
216
+ non_none_type = args[0] if args[1] is type(None) else args[1]
217
+ return f"Optional[{_get_type_string(non_none_type, quote_config_meta)}]"
218
+ return f"Union[{', '.join(_get_type_string(arg, quote_config_meta) for arg in args)}]"
219
+
220
+ # Default case - use the type name
221
+ return getattr(field_type, "__name__", str(field_type))
222
+
223
+
224
+ def generate_typed_classes_module(
225
+ config_classes: List[Type], module_name: str = "typed_configs"
226
+ ) -> str:
227
+ """
228
+ Generate a complete Python module with typed classes for multiple ConfigMeta classes.
229
+
230
+ Args:
231
+ config_classes: List of ConfigMeta classes
232
+ module_name: Name for the generated module
233
+
234
+ Returns:
235
+ Complete Python module code
236
+ """
237
+ imports = [
238
+ "from typing import Optional, List, Dict, Any",
239
+ "from .unified_config import "
240
+ + ", ".join(cls.__name__ for cls in config_classes),
241
+ TYPED_DICT_IMPORT,
242
+ ]
243
+
244
+ class_codes = []
245
+ for config_class in config_classes:
246
+ class_codes.append(generate_typed_class_code(config_class))
247
+
248
+ # Use string concatenation instead of f-string with backslashes
249
+ newline = "\n"
250
+ module_code = (
251
+ '"""'
252
+ + newline
253
+ + "Auto-generated typed classes for ConfigMeta classes."
254
+ + newline
255
+ + newline
256
+ + "This module provides IDE-friendly typed interfaces for all configuration classes."
257
+ + newline
258
+ + "The reason we auto-generate this file is because we want to provide a bridge between what is the ConfigMeta classes and the typed programmatic interface."
259
+ + newline
260
+ + "The CoreConfig class is setup in a way that if any additionally params are missed out from being auto-generated "
261
+ + "then it will not affect the core functionality of the programmatic API."
262
+ + newline
263
+ + "The new parameters will just not show up in IDE autocompletions."
264
+ + newline
265
+ + "It is fine if this file is not regularly updated by running the script in the .pre-commit-config.app-changes.yaml"
266
+ + newline
267
+ + "but it is recommended that this file not be deleted or manually edited."
268
+ + newline
269
+ + newline
270
+ + '"""'
271
+ + newline
272
+ + newline
273
+ + newline.join(imports)
274
+ + newline
275
+ + newline
276
+ + (newline + newline).join(class_codes)
277
+ + newline
278
+ )
279
+
280
+ return module_code
281
+
282
+
283
+ def create_typed_init_class_dynamic(config_class: Type) -> Type:
284
+ """
285
+ Dynamically create a typed init class with proper IDE support.
286
+
287
+ This creates the class at runtime but with proper type annotations
288
+ that IDEs can understand.
289
+ """
290
+ if not hasattr(config_class, "_fields"):
291
+ raise ValueError(f"Class {config_class.__name__} is not a ConfigMeta class")
292
+
293
+ class_name = f"Typed{config_class.__name__}"
294
+
295
+ # Create the init method with proper signature
296
+ def create_init_method():
297
+ # Build the signature dynamically
298
+ sig_params = []
299
+ annotations: Dict[str, Any] = {"return": type(None)}
300
+
301
+ for field_name, field_info in config_class._fields.items():
302
+ field_type = field_info.field_type
303
+
304
+ # Handle nested ConfigMeta classes
305
+ if ConfigMeta.is_instance(field_type):
306
+ field_type = Dict[str, Any] # Use Dict for nested configs
307
+
308
+ # Handle Optional fields
309
+ if not field_info.required:
310
+ field_type = Optional[field_type]
311
+
312
+ annotations[field_name] = field_type
313
+
314
+ def __init__(self, **kwargs):
315
+ # Validate kwargs
316
+ required_fields = {
317
+ name for name, info in config_class._fields.items() if info.required
318
+ }
319
+ provided_fields = set(kwargs.keys())
320
+ valid_fields = set(config_class._fields.keys())
321
+
322
+ # Check required fields
323
+ missing_fields = required_fields - provided_fields
324
+ if missing_fields:
325
+ raise ValueError(
326
+ f"Missing required fields: {', '.join(missing_fields)}"
327
+ )
328
+
329
+ # Check for unknown fields - but allow them for flexibility
330
+ unknown_fields = provided_fields - valid_fields
331
+ if unknown_fields:
332
+ print(
333
+ f"Warning: Unknown fields will be passed through: {', '.join(unknown_fields)}"
334
+ )
335
+
336
+ self._kwargs = kwargs
337
+ self._config_class = config_class
338
+
339
+ # Set annotations
340
+ __init__.__annotations__ = annotations
341
+ return __init__
342
+
343
+ def create_config(self):
344
+ """Create and return the ConfigMeta class instance."""
345
+ return config_class.from_dict(self._kwargs)
346
+
347
+ def to_dict(self) -> Dict[str, Any]:
348
+ """Return the raw kwargs as a dictionary."""
349
+ return self._kwargs.copy()
350
+
351
+ def __repr__(self) -> str:
352
+ return f"{class_name}({self._kwargs})"
353
+
354
+ # Create the class
355
+ init_method = create_init_method()
356
+
357
+ TypedClass = type(
358
+ class_name,
359
+ (object,),
360
+ {
361
+ "__init__": init_method,
362
+ "create_config": create_config,
363
+ "to_dict": to_dict,
364
+ "__repr__": __repr__,
365
+ "__module__": __name__,
366
+ "__qualname__": class_name,
367
+ },
368
+ )
369
+
370
+ return TypedClass
371
+
372
+
373
+ # Auto-generate and write typed classes to a file
374
+ def generate_typed_classes_file(output_file: Optional[str] = None):
375
+ """
376
+ Generate typed classes and write them to a file for IDE support.
377
+
378
+ Args:
379
+ output_file: Path to write the generated classes. If None, prints to stdout.
380
+ """
381
+ from .unified_config import CoreConfig
382
+
383
+ config_classes = [CoreConfig]
384
+
385
+ module_code = generate_typed_classes_module(config_classes)
386
+
387
+ if output_file:
388
+ with open(output_file, "w") as f:
389
+ f.write(module_code)
390
+ print(f"Generated typed classes written to {output_file}")
391
+ else:
392
+ print(module_code)
393
+
394
+
395
+ # Example usage and testing
396
+ if __name__ == "__main__":
397
+ # Generate typed classes file
398
+ generate_typed_classes_file(os.path.join(current_dir, "typed_configs.py"))