flyte 0.2.0b1__py3-none-any.whl → 2.0.0b46__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 (266) hide show
  1. flyte/__init__.py +83 -30
  2. flyte/_bin/connect.py +61 -0
  3. flyte/_bin/debug.py +38 -0
  4. flyte/_bin/runtime.py +87 -19
  5. flyte/_bin/serve.py +351 -0
  6. flyte/_build.py +3 -2
  7. flyte/_cache/cache.py +6 -5
  8. flyte/_cache/local_cache.py +216 -0
  9. flyte/_code_bundle/_ignore.py +31 -5
  10. flyte/_code_bundle/_packaging.py +42 -11
  11. flyte/_code_bundle/_utils.py +57 -34
  12. flyte/_code_bundle/bundle.py +130 -27
  13. flyte/_constants.py +1 -0
  14. flyte/_context.py +21 -5
  15. flyte/_custom_context.py +73 -0
  16. flyte/_debug/constants.py +37 -0
  17. flyte/_debug/utils.py +17 -0
  18. flyte/_debug/vscode.py +315 -0
  19. flyte/_deploy.py +396 -75
  20. flyte/_deployer.py +109 -0
  21. flyte/_environment.py +94 -11
  22. flyte/_excepthook.py +37 -0
  23. flyte/_group.py +2 -1
  24. flyte/_hash.py +1 -16
  25. flyte/_image.py +544 -231
  26. flyte/_initialize.py +456 -316
  27. flyte/_interface.py +40 -5
  28. flyte/_internal/controllers/__init__.py +22 -8
  29. flyte/_internal/controllers/_local_controller.py +159 -35
  30. flyte/_internal/controllers/_trace.py +18 -10
  31. flyte/_internal/controllers/remote/__init__.py +38 -9
  32. flyte/_internal/controllers/remote/_action.py +82 -12
  33. flyte/_internal/controllers/remote/_client.py +6 -2
  34. flyte/_internal/controllers/remote/_controller.py +290 -64
  35. flyte/_internal/controllers/remote/_core.py +155 -95
  36. flyte/_internal/controllers/remote/_informer.py +40 -20
  37. flyte/_internal/controllers/remote/_service_protocol.py +2 -2
  38. flyte/_internal/imagebuild/__init__.py +2 -10
  39. flyte/_internal/imagebuild/docker_builder.py +391 -84
  40. flyte/_internal/imagebuild/image_builder.py +111 -55
  41. flyte/_internal/imagebuild/remote_builder.py +409 -0
  42. flyte/_internal/imagebuild/utils.py +79 -0
  43. flyte/_internal/resolvers/_app_env_module.py +92 -0
  44. flyte/_internal/resolvers/_task_module.py +5 -38
  45. flyte/_internal/resolvers/app_env.py +26 -0
  46. flyte/_internal/resolvers/common.py +8 -1
  47. flyte/_internal/resolvers/default.py +2 -2
  48. flyte/_internal/runtime/convert.py +319 -36
  49. flyte/_internal/runtime/entrypoints.py +106 -18
  50. flyte/_internal/runtime/io.py +71 -23
  51. flyte/_internal/runtime/resources_serde.py +21 -7
  52. flyte/_internal/runtime/reuse.py +125 -0
  53. flyte/_internal/runtime/rusty.py +196 -0
  54. flyte/_internal/runtime/task_serde.py +239 -66
  55. flyte/_internal/runtime/taskrunner.py +48 -8
  56. flyte/_internal/runtime/trigger_serde.py +162 -0
  57. flyte/_internal/runtime/types_serde.py +7 -16
  58. flyte/_keyring/file.py +115 -0
  59. flyte/_link.py +30 -0
  60. flyte/_logging.py +241 -42
  61. flyte/_map.py +312 -0
  62. flyte/_metrics.py +59 -0
  63. flyte/_module.py +74 -0
  64. flyte/_pod.py +30 -0
  65. flyte/_resources.py +296 -33
  66. flyte/_retry.py +1 -7
  67. flyte/_reusable_environment.py +72 -7
  68. flyte/_run.py +462 -132
  69. flyte/_secret.py +47 -11
  70. flyte/_serve.py +333 -0
  71. flyte/_task.py +245 -56
  72. flyte/_task_environment.py +219 -97
  73. flyte/_task_plugins.py +47 -0
  74. flyte/_tools.py +8 -8
  75. flyte/_trace.py +15 -24
  76. flyte/_trigger.py +1027 -0
  77. flyte/_utils/__init__.py +12 -1
  78. flyte/_utils/asyn.py +3 -1
  79. flyte/_utils/async_cache.py +139 -0
  80. flyte/_utils/coro_management.py +5 -4
  81. flyte/_utils/description_parser.py +19 -0
  82. flyte/_utils/docker_credentials.py +173 -0
  83. flyte/_utils/helpers.py +45 -19
  84. flyte/_utils/module_loader.py +123 -0
  85. flyte/_utils/org_discovery.py +57 -0
  86. flyte/_utils/uv_script_parser.py +8 -1
  87. flyte/_version.py +16 -3
  88. flyte/app/__init__.py +27 -0
  89. flyte/app/_app_environment.py +362 -0
  90. flyte/app/_connector_environment.py +40 -0
  91. flyte/app/_deploy.py +130 -0
  92. flyte/app/_parameter.py +343 -0
  93. flyte/app/_runtime/__init__.py +3 -0
  94. flyte/app/_runtime/app_serde.py +383 -0
  95. flyte/app/_types.py +113 -0
  96. flyte/app/extras/__init__.py +9 -0
  97. flyte/app/extras/_auth_middleware.py +217 -0
  98. flyte/app/extras/_fastapi.py +93 -0
  99. flyte/app/extras/_model_loader/__init__.py +3 -0
  100. flyte/app/extras/_model_loader/config.py +7 -0
  101. flyte/app/extras/_model_loader/loader.py +288 -0
  102. flyte/cli/__init__.py +12 -0
  103. flyte/cli/_abort.py +28 -0
  104. flyte/cli/_build.py +114 -0
  105. flyte/cli/_common.py +493 -0
  106. flyte/cli/_create.py +371 -0
  107. flyte/cli/_delete.py +45 -0
  108. flyte/cli/_deploy.py +401 -0
  109. flyte/cli/_gen.py +316 -0
  110. flyte/cli/_get.py +446 -0
  111. flyte/cli/_option.py +33 -0
  112. flyte/{_cli → cli}/_params.py +57 -17
  113. flyte/cli/_plugins.py +209 -0
  114. flyte/cli/_prefetch.py +292 -0
  115. flyte/cli/_run.py +690 -0
  116. flyte/cli/_serve.py +338 -0
  117. flyte/cli/_update.py +86 -0
  118. flyte/cli/_user.py +20 -0
  119. flyte/cli/main.py +246 -0
  120. flyte/config/__init__.py +2 -167
  121. flyte/config/_config.py +215 -163
  122. flyte/config/_internal.py +10 -1
  123. flyte/config/_reader.py +225 -0
  124. flyte/connectors/__init__.py +11 -0
  125. flyte/connectors/_connector.py +330 -0
  126. flyte/connectors/_server.py +194 -0
  127. flyte/connectors/utils.py +159 -0
  128. flyte/errors.py +134 -2
  129. flyte/extend.py +24 -0
  130. flyte/extras/_container.py +69 -56
  131. flyte/git/__init__.py +3 -0
  132. flyte/git/_config.py +279 -0
  133. flyte/io/__init__.py +8 -1
  134. flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
  135. flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
  136. flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
  137. flyte/io/_dir.py +575 -113
  138. flyte/io/_file.py +587 -141
  139. flyte/io/_hashing_io.py +342 -0
  140. flyte/io/extend.py +7 -0
  141. flyte/models.py +635 -0
  142. flyte/prefetch/__init__.py +22 -0
  143. flyte/prefetch/_hf_model.py +563 -0
  144. flyte/remote/__init__.py +14 -3
  145. flyte/remote/_action.py +879 -0
  146. flyte/remote/_app.py +346 -0
  147. flyte/remote/_auth_metadata.py +42 -0
  148. flyte/remote/_client/_protocols.py +62 -4
  149. flyte/remote/_client/auth/_auth_utils.py +19 -0
  150. flyte/remote/_client/auth/_authenticators/base.py +8 -2
  151. flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
  152. flyte/remote/_client/auth/_authenticators/factory.py +4 -0
  153. flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
  154. flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
  155. flyte/remote/_client/auth/_channel.py +47 -18
  156. flyte/remote/_client/auth/_client_config.py +5 -3
  157. flyte/remote/_client/auth/_keyring.py +15 -2
  158. flyte/remote/_client/auth/_token_client.py +3 -3
  159. flyte/remote/_client/controlplane.py +206 -18
  160. flyte/remote/_common.py +66 -0
  161. flyte/remote/_data.py +107 -22
  162. flyte/remote/_logs.py +116 -33
  163. flyte/remote/_project.py +21 -19
  164. flyte/remote/_run.py +164 -631
  165. flyte/remote/_secret.py +72 -29
  166. flyte/remote/_task.py +387 -46
  167. flyte/remote/_trigger.py +368 -0
  168. flyte/remote/_user.py +43 -0
  169. flyte/report/_report.py +10 -6
  170. flyte/storage/__init__.py +13 -1
  171. flyte/storage/_config.py +237 -0
  172. flyte/storage/_parallel_reader.py +289 -0
  173. flyte/storage/_storage.py +268 -59
  174. flyte/syncify/__init__.py +56 -0
  175. flyte/syncify/_api.py +414 -0
  176. flyte/types/__init__.py +39 -0
  177. flyte/types/_interface.py +22 -7
  178. flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
  179. flyte/types/_string_literals.py +8 -9
  180. flyte/types/_type_engine.py +226 -126
  181. flyte/types/_utils.py +1 -1
  182. flyte-2.0.0b46.data/scripts/debug.py +38 -0
  183. flyte-2.0.0b46.data/scripts/runtime.py +194 -0
  184. flyte-2.0.0b46.dist-info/METADATA +352 -0
  185. flyte-2.0.0b46.dist-info/RECORD +221 -0
  186. flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
  187. flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
  188. flyte/_api_commons.py +0 -3
  189. flyte/_cli/_common.py +0 -299
  190. flyte/_cli/_create.py +0 -42
  191. flyte/_cli/_delete.py +0 -23
  192. flyte/_cli/_deploy.py +0 -140
  193. flyte/_cli/_get.py +0 -235
  194. flyte/_cli/_run.py +0 -174
  195. flyte/_cli/main.py +0 -98
  196. flyte/_datastructures.py +0 -342
  197. flyte/_internal/controllers/pbhash.py +0 -39
  198. flyte/_protos/common/authorization_pb2.py +0 -66
  199. flyte/_protos/common/authorization_pb2.pyi +0 -108
  200. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  201. flyte/_protos/common/identifier_pb2.py +0 -71
  202. flyte/_protos/common/identifier_pb2.pyi +0 -82
  203. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  204. flyte/_protos/common/identity_pb2.py +0 -48
  205. flyte/_protos/common/identity_pb2.pyi +0 -72
  206. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  207. flyte/_protos/common/list_pb2.py +0 -36
  208. flyte/_protos/common/list_pb2.pyi +0 -69
  209. flyte/_protos/common/list_pb2_grpc.py +0 -4
  210. flyte/_protos/common/policy_pb2.py +0 -37
  211. flyte/_protos/common/policy_pb2.pyi +0 -27
  212. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  213. flyte/_protos/common/role_pb2.py +0 -37
  214. flyte/_protos/common/role_pb2.pyi +0 -53
  215. flyte/_protos/common/role_pb2_grpc.py +0 -4
  216. flyte/_protos/common/runtime_version_pb2.py +0 -28
  217. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  218. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  219. flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
  220. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  221. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  222. flyte/_protos/secret/definition_pb2.py +0 -49
  223. flyte/_protos/secret/definition_pb2.pyi +0 -93
  224. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  225. flyte/_protos/secret/payload_pb2.py +0 -62
  226. flyte/_protos/secret/payload_pb2.pyi +0 -94
  227. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  228. flyte/_protos/secret/secret_pb2.py +0 -38
  229. flyte/_protos/secret/secret_pb2.pyi +0 -6
  230. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  231. flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
  232. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  233. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  234. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  235. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  236. flyte/_protos/workflow/queue_service_pb2.py +0 -106
  237. flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
  238. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  239. flyte/_protos/workflow/run_definition_pb2.py +0 -128
  240. flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
  241. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  242. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  243. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  244. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  245. flyte/_protos/workflow/run_service_pb2.py +0 -133
  246. flyte/_protos/workflow/run_service_pb2.pyi +0 -175
  247. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
  248. flyte/_protos/workflow/state_service_pb2.py +0 -58
  249. flyte/_protos/workflow/state_service_pb2.pyi +0 -71
  250. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  251. flyte/_protos/workflow/task_definition_pb2.py +0 -72
  252. flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
  253. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  254. flyte/_protos/workflow/task_service_pb2.py +0 -44
  255. flyte/_protos/workflow/task_service_pb2.pyi +0 -31
  256. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
  257. flyte/io/_dataframe.py +0 -0
  258. flyte/io/pickle/__init__.py +0 -0
  259. flyte/remote/_console.py +0 -18
  260. flyte-0.2.0b1.dist-info/METADATA +0 -179
  261. flyte-0.2.0b1.dist-info/RECORD +0 -204
  262. flyte-0.2.0b1.dist-info/entry_points.txt +0 -3
  263. /flyte/{_cli → _debug}/__init__.py +0 -0
  264. /flyte/{_protos → _keyring}/__init__.py +0 -0
  265. {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
  266. {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import pathlib
2
2
  import re
3
3
  from dataclasses import dataclass, field
4
+ from functools import lru_cache
4
5
  from typing import Dict, List, Optional
5
6
 
6
7
  import toml
@@ -15,6 +16,7 @@ class ToolUVConfig:
15
16
  class UVScriptMetadata:
16
17
  requires_python: Optional[str] = None
17
18
  dependencies: List[str] = field(default_factory=list)
19
+ pyprojects: List[str] = field(default_factory=list)
18
20
  tool: Optional[Dict[str, ToolUVConfig]] = None
19
21
 
20
22
 
@@ -27,6 +29,7 @@ def _extract_uv_metadata_block(text: str) -> str | None:
27
29
  return "\n".join(lines)
28
30
 
29
31
 
32
+ @lru_cache
30
33
  def parse_uv_script_file(path: pathlib.Path) -> UVScriptMetadata:
31
34
  if not path.exists() or not path.is_file():
32
35
  raise FileNotFoundError(f"File not found: {path}")
@@ -42,8 +45,12 @@ def parse_uv_script_file(path: pathlib.Path) -> UVScriptMetadata:
42
45
  raise ValueError(f"Invalid TOML in metadata block: {e}")
43
46
 
44
47
  tool_data = data.get("tool", {}).get("uv", {})
48
+ dependencies = data.get("dependencies", [])
49
+ py_dependencies = [dep for dep in dependencies if "file://" not in dep]
50
+ pyprojects = [dep.split("file://")[-1] for dep in dependencies if "file://" in dep]
45
51
  return UVScriptMetadata(
46
52
  requires_python=data.get("requires-python"),
47
- dependencies=data.get("dependencies", []),
53
+ dependencies=py_dependencies,
54
+ pyprojects=pyprojects,
48
55
  tool={"uv": ToolUVConfig(exclude_newer=tool_data.get("exclude-newer"))} if tool_data else None,
49
56
  )
flyte/_version.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '0.2.0b1'
21
- __version_tuple__ = version_tuple = (0, 2, 0, 'b1')
31
+ __version__ = version = '2.0.0b46'
32
+ __version_tuple__ = version_tuple = (2, 0, 0, 'b46')
33
+
34
+ __commit_id__ = commit_id = 'g9b41862a0'
flyte/app/__init__.py ADDED
@@ -0,0 +1,27 @@
1
+ from flyte.app._app_environment import AppEnvironment
2
+ from flyte.app._connector_environment import ConnectorEnvironment
3
+ from flyte.app._parameter import AppEndpoint, Parameter, RunOutput, get_parameter
4
+ from flyte.app._types import Domain, Link, Port, Scaling
5
+
6
+ __all__ = [
7
+ "AppEndpoint",
8
+ "AppEnvironment",
9
+ "ConnectorEnvironment",
10
+ "Domain",
11
+ "Link",
12
+ "Parameter",
13
+ "Port",
14
+ "RunOutput",
15
+ "Scaling",
16
+ "get_parameter",
17
+ ]
18
+
19
+
20
+ def register_app_deployer():
21
+ from flyte import _deployer as deployer
22
+ from flyte.app._deploy import _deploy_app_env
23
+
24
+ deployer.register_deployer(AppEnvironment, _deploy_app_env)
25
+
26
+
27
+ register_app_deployer()
@@ -0,0 +1,362 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import os
5
+ import re
6
+ import shlex
7
+ from dataclasses import dataclass, field, replace
8
+ from typing import TYPE_CHECKING, Any, Callable, List, Literal, Optional, Union
9
+
10
+ import rich.repr
11
+
12
+ from flyte import Environment, Image, Resources, SecretRequest
13
+ from flyte.app._parameter import Parameter
14
+ from flyte.app._types import Domain, Link, Port, Scaling
15
+ from flyte.models import SerializationContext
16
+
17
+ if TYPE_CHECKING:
18
+ pass
19
+
20
+
21
+ APP_NAME_RE = re.compile(r"[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*")
22
+ INVALID_APP_PORTS = [8012, 8022, 8112, 9090, 9091]
23
+ INTERNAL_APP_ENDPOINT_PATTERN_ENV_VAR = "INTERNAL_APP_ENDPOINT_PATTERN"
24
+
25
+
26
+ @rich.repr.auto
27
+ @dataclass(init=True, repr=True)
28
+ class AppEnvironment(Environment):
29
+ """
30
+ :param type: Type of the environment.
31
+ :param port: Port to use for the app server.
32
+ :param args: Arguments to pass to app.
33
+ :param command: Command to run in the app.
34
+ :param requires_auth: Whether the app requires authentication.
35
+ :param scaling: Scaling configuration for the app environment.
36
+ :param domain: Domain to use for the app.
37
+ :param links: Links to other environments.
38
+ :param include: Files to include in the environment to run the app.
39
+ :param parameters: Parameters to pass to the app environment.
40
+ :param cluster_pool: Cluster pool to use for the app environment.
41
+ :param name: Name of the app environment
42
+ :param image: Docker image to use for the environment. If set to "auto", will use the default image.
43
+ :param resources: Resources to allocate for the environment.
44
+ :param env_vars: Environment variables to set for the environment.
45
+ :param secrets: Secrets to inject into the environment.
46
+ :param depends_on: Environment dependencies to hint, so when you deploy the environment, the dependencies are
47
+ also deployed. This is useful when you have a set of environments that depend on each other.
48
+ """
49
+
50
+ type: Optional[str] = None
51
+ port: int | Port = 8080
52
+ args: Optional[Union[List[str], str]] = None
53
+ command: Optional[Union[List[str], str]] = None
54
+ requires_auth: bool = True
55
+ scaling: Scaling = field(default_factory=Scaling)
56
+ domain: Domain | None = field(default_factory=Domain)
57
+ # Integration
58
+ links: List[Link] = field(default_factory=list)
59
+
60
+ # Code
61
+ include: List[str] = field(default_factory=list)
62
+ parameters: List[Parameter] = field(default_factory=list)
63
+
64
+ # queue / cluster_pool
65
+ cluster_pool: str = "default"
66
+
67
+ # private field
68
+ _server: Callable[[], None] | None = field(init=False, default=None)
69
+ _on_startup: Callable[[], None] | None = field(init=False, default=None)
70
+ _on_shutdown: Callable[[], None] | None = field(init=False, default=None)
71
+
72
+ def _validate_name(self):
73
+ if not APP_NAME_RE.fullmatch(self.name):
74
+ raise ValueError(
75
+ f"App name '{self.name}' must consist of lower case alphanumeric characters or '-', "
76
+ "and must start and end with an alphanumeric character."
77
+ )
78
+
79
+ def _get_app_filename(self) -> str:
80
+ """
81
+ Get the filename of the file that declares this app environment.
82
+
83
+ Returns the actual file path instead of <string>, skipping flyte SDK internal files.
84
+ """
85
+
86
+ def is_user_file(filename: str) -> bool:
87
+ """Check if a file is a user file (not part of flyte SDK)."""
88
+ if filename in ("<string>", "<stdin>"):
89
+ return False
90
+ if not os.path.exists(filename):
91
+ return False
92
+ # Skip files that are part of the flyte SDK
93
+ abs_path = os.path.abspath(filename)
94
+ # Check if file is in flyte package
95
+ return ("site-packages/flyte" not in abs_path and "/flyte/" not in abs_path) or "/examples/" in abs_path
96
+
97
+ # Try frame inspection first - walk up the stack to find user file
98
+ frame = inspect.currentframe()
99
+ while frame is not None:
100
+ filename = frame.f_code.co_filename
101
+ if is_user_file(filename):
102
+ return os.path.abspath(filename)
103
+ frame = frame.f_back
104
+
105
+ # Fallback: Inspect the full stack to find the first user file
106
+ stack = inspect.stack()
107
+ for frame_info in stack:
108
+ filename = frame_info.filename
109
+ if is_user_file(filename):
110
+ return os.path.abspath(filename)
111
+
112
+ # Last fallback: Try to get from __main__ module
113
+ import sys
114
+
115
+ if hasattr(sys.modules.get("__main__"), "__file__"):
116
+ main_file = sys.modules["__main__"].__file__
117
+ if main_file and os.path.exists(main_file):
118
+ return os.path.abspath(main_file)
119
+
120
+ # Last resort: return the current working directory with a placeholder
121
+ # This shouldn't happen in normal usage
122
+ return os.path.join(os.getcwd(), "app.py")
123
+
124
+ def __post_init__(self):
125
+ super().__post_init__()
126
+ if self.args is not None and not isinstance(self.args, (list, str)):
127
+ raise TypeError(f"Expected args to be of type List[str] or str, got {type(self.args)}")
128
+ if isinstance(self.port, int):
129
+ self.port = Port(port=self.port) # Name should be blank can be h2c / http1
130
+ if self.port.port in INVALID_APP_PORTS:
131
+ raise ValueError(f"Port {self.port.port} is reserved and cannot be used for AppEnvironment")
132
+ if self.command is not None and not isinstance(self.command, (list, str)):
133
+ raise TypeError(f"Expected command to be of type List[str] or str, got {type(self.command)}")
134
+ if not isinstance(self.scaling, Scaling):
135
+ raise TypeError(f"Expected scaling to be of type Scaling, got {type(self.scaling)}")
136
+ if not isinstance(self.domain, (Domain, type(None))):
137
+ raise TypeError(f"Expected domain to be of type Domain or None, got {type(self.domain)}")
138
+ for link in self.links:
139
+ if not isinstance(link, Link):
140
+ raise TypeError(f"Expected links to be of type List[Link], got {type(link)}")
141
+
142
+ self._validate_name()
143
+
144
+ # get instantiated file to keep track of app root directory
145
+ self._app_filename = self._get_app_filename()
146
+
147
+ # Capture the frame where this environment was instantiated
148
+ # This helps us find the module where the app variable is defined
149
+ frame = inspect.currentframe()
150
+ if frame and frame.f_back:
151
+ # Go up the call stack to find the user's module
152
+ # Skip the dataclass __init__ frame
153
+ caller_frame = frame.f_back
154
+ if caller_frame and caller_frame.f_back:
155
+ self._caller_frame = inspect.getframeinfo(caller_frame.f_back)
156
+
157
+ def container_args(self, serialize_context: SerializationContext) -> List[str]:
158
+ if self.args is None:
159
+ return []
160
+ elif isinstance(self.args, str):
161
+ return shlex.split(self.args)
162
+ else:
163
+ # args is a list
164
+ return self.args
165
+
166
+ def _serialize_parameters(self, parameter_overrides: list[Parameter] | None) -> str:
167
+ if not self.parameters:
168
+ return ""
169
+ from ._parameter import SerializableParameterCollection
170
+
171
+ serialized_parameters = SerializableParameterCollection.from_parameters(parameter_overrides or self.parameters)
172
+ return serialized_parameters.to_transport
173
+
174
+ def on_startup(self, fn: Callable[..., None]) -> Callable[..., None]:
175
+ """
176
+ Decorator to define the startup function for the app environment.
177
+
178
+ This function is called before the server function is called.
179
+
180
+ The decorated function can be a sync or async function, and accepts input
181
+ parameters based on the Parameters defined in the AppEnvironment
182
+ definition.
183
+ """
184
+ self._on_startup = fn
185
+ return self._on_startup
186
+
187
+ def server(self, fn: Callable[..., None]) -> Callable[..., None]:
188
+ """
189
+ Decorator to define the server function for the app environment.
190
+
191
+ This decorated function can be a sync or async function, and accepts input
192
+ parameters based on the Parameters defined in the AppEnvironment
193
+ definition.
194
+ """
195
+ self._server = fn
196
+ return self._server
197
+
198
+ def on_shutdown(self, fn: Callable[..., None]) -> Callable[..., None]:
199
+ """
200
+ Decorator to define the shutdown function for the app environment.
201
+
202
+ This function is called after the server function is called.
203
+
204
+ This decorated function can be a sync or async function, and accepts input
205
+ parameters based on the Parameters defined in the AppEnvironment
206
+ definition.
207
+ """
208
+ self._on_shutdown = fn
209
+ return self._on_shutdown
210
+
211
+ def container_cmd(
212
+ self, serialize_context: SerializationContext, parameter_overrides: list[Parameter] | None = None
213
+ ) -> List[str]:
214
+ from flyte._internal.resolvers.app_env import AppEnvResolver
215
+
216
+ if self.command is None:
217
+ # Default command
218
+ version = serialize_context.version
219
+ if version is None and serialize_context.code_bundle is not None:
220
+ version = serialize_context.code_bundle.computed_version
221
+
222
+ cmd: list[str] = [
223
+ "fserve",
224
+ "--version",
225
+ version or "",
226
+ "--project",
227
+ serialize_context.project or "",
228
+ "--domain",
229
+ serialize_context.domain or "",
230
+ "--org",
231
+ serialize_context.org or "",
232
+ ]
233
+
234
+ if serialize_context.image_cache and serialize_context.image_cache.serialized_form:
235
+ cmd = [*cmd, "--image-cache", serialize_context.image_cache.serialized_form]
236
+ else:
237
+ if serialize_context.image_cache:
238
+ cmd = [*cmd, "--image-cache", serialize_context.image_cache.to_transport]
239
+
240
+ if serialize_context.code_bundle:
241
+ if serialize_context.code_bundle.tgz:
242
+ cmd = [*cmd, *["--tgz", f"{serialize_context.code_bundle.tgz}"]]
243
+ elif serialize_context.code_bundle.pkl:
244
+ cmd = [*cmd, *["--pkl", f"{serialize_context.code_bundle.pkl}"]]
245
+ cmd = [*cmd, *["--dest", f"{serialize_context.code_bundle.destination or '.'}"]]
246
+
247
+ if self.parameters:
248
+ cmd.append("--parameters")
249
+ cmd.append(self._serialize_parameters(parameter_overrides))
250
+
251
+ # Only add resolver args if _caller_frame is set and we can extract the module
252
+ # (i.e., app was created in a module and can be found)
253
+ if self._caller_frame is not None:
254
+ assert serialize_context.root_dir is not None
255
+ try:
256
+ _app_env_resolver = AppEnvResolver()
257
+ loader_args = _app_env_resolver.loader_args(self, serialize_context.root_dir)
258
+ cmd = [
259
+ *cmd,
260
+ *[
261
+ "--resolver",
262
+ _app_env_resolver.import_path,
263
+ "--resolver-args",
264
+ loader_args,
265
+ ],
266
+ ]
267
+ except RuntimeError as e:
268
+ # If we can't find the app in the module (e.g., in tests), skip resolver args
269
+ from flyte._logging import logger
270
+
271
+ logger.warning(f"Failed to extract app resolver args: {e}. Skipping resolver args.")
272
+ return [*cmd, "--"]
273
+ elif isinstance(self.command, str):
274
+ return shlex.split(self.command)
275
+ else:
276
+ # command is a list
277
+ return self.command
278
+
279
+ def get_port(self) -> Port:
280
+ if isinstance(self.port, int):
281
+ self.port = Port(port=self.port)
282
+ return self.port
283
+
284
+ @property
285
+ def endpoint(self) -> str:
286
+ endpoint_pattern = os.getenv(INTERNAL_APP_ENDPOINT_PATTERN_ENV_VAR)
287
+ if endpoint_pattern is not None:
288
+ return endpoint_pattern.format(app_fqdn=self.name)
289
+
290
+ import flyte.remote
291
+ from flyte._initialize import ensure_client
292
+
293
+ ensure_client()
294
+ app = flyte.remote.App.get(name=self.name)
295
+ return app.endpoint
296
+
297
+ def clone_with(
298
+ self,
299
+ name: str,
300
+ image: Optional[Union[str, Image, Literal["auto"]]] = None,
301
+ resources: Optional[Resources] = None,
302
+ env_vars: Optional[dict[str, str]] = None,
303
+ secrets: Optional[SecretRequest] = None,
304
+ depends_on: Optional[List[Environment]] = None,
305
+ description: Optional[str] = None,
306
+ interruptible: Optional[bool] = None,
307
+ **kwargs: Any,
308
+ ) -> AppEnvironment:
309
+ # validate unknown kwargs if needed
310
+
311
+ type = kwargs.pop("type", None)
312
+ port = kwargs.pop("port", None)
313
+ args = kwargs.pop("args", None)
314
+ command = kwargs.pop("command", None)
315
+ requires_auth = kwargs.pop("requires_auth", None)
316
+ scaling = kwargs.pop("scaling", None)
317
+ domain = kwargs.pop("domain", None)
318
+ links = kwargs.pop("links", None)
319
+ include = kwargs.pop("include", None)
320
+ parameters = kwargs.pop("parameters", None)
321
+ cluster_pool = kwargs.pop("cluster_pool", None)
322
+
323
+ if kwargs:
324
+ raise TypeError(f"Unexpected keyword arguments: {list(kwargs.keys())}")
325
+
326
+ kwargs = self._get_kwargs()
327
+ kwargs["name"] = name
328
+ if image is not None:
329
+ kwargs["image"] = image
330
+ if resources is not None:
331
+ kwargs["resources"] = resources
332
+ if env_vars is not None:
333
+ kwargs["env_vars"] = env_vars
334
+ if secrets is not None:
335
+ kwargs["secrets"] = secrets
336
+ if depends_on is not None:
337
+ kwargs["depends_on"] = depends_on
338
+ if description is not None:
339
+ kwargs["description"] = description
340
+ if type is not None:
341
+ kwargs["type"] = type
342
+ if port is not None:
343
+ kwargs["port"] = port
344
+ if args is not None:
345
+ kwargs["args"] = args
346
+ if command is not None:
347
+ kwargs["command"] = command
348
+ if requires_auth is not None:
349
+ kwargs["requires_auth"] = requires_auth
350
+ if scaling is not None:
351
+ kwargs["scaling"] = scaling
352
+ if domain is not None:
353
+ kwargs["domain"] = domain
354
+ if links is not None:
355
+ kwargs["links"] = links
356
+ if include is not None:
357
+ kwargs["include"] = include
358
+ if parameters is not None:
359
+ kwargs["parameters"] = parameters
360
+ if cluster_pool is not None:
361
+ kwargs["cluster_pool"] = cluster_pool
362
+ return replace(self, **kwargs)
@@ -0,0 +1,40 @@
1
+ import shlex
2
+ from dataclasses import dataclass, field
3
+ from typing import List
4
+
5
+ import rich.repr
6
+
7
+ from flyte.app import AppEnvironment
8
+ from flyte.app._parameter import Parameter
9
+ from flyte.app._types import Port
10
+ from flyte.models import SerializationContext
11
+
12
+
13
+ @rich.repr.auto
14
+ @dataclass(init=True, repr=True)
15
+ class ConnectorEnvironment(AppEnvironment):
16
+ type: str = "Connector"
17
+ port: int | Port = field(default=Port(port=8080, name="h2c"))
18
+
19
+ def __post_init__(self):
20
+ super().__post_init__()
21
+
22
+ def container_args(self, serialize_context: SerializationContext) -> List[str]:
23
+ if self.args is None:
24
+ if isinstance(self.port, Port):
25
+ port = self.port.port
26
+ else:
27
+ port = self.port
28
+ return ["c0", "--port", str(port), "--prometheus_port", "9092"]
29
+ return super().container_args(serialize_context)
30
+
31
+ def container_cmd(
32
+ self, serialize_context: SerializationContext, parameter_overrides: list[Parameter] | None = None
33
+ ) -> List[str]:
34
+ if isinstance(self.command, str):
35
+ return shlex.split(self.command)
36
+ elif isinstance(self.command, list):
37
+ return self.command
38
+ else:
39
+ # command is None, use default from parent class
40
+ return super().container_cmd(serialize_context, parameter_overrides)
flyte/app/_deploy.py ADDED
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ import flyte._deployer as deployer
8
+ from flyte import Image
9
+ from flyte._code_bundle.bundle import build_code_bundle_from_relative_paths
10
+ from flyte._initialize import ensure_client
11
+ from flyte._logging import logger
12
+ from flyte.models import SerializationContext
13
+
14
+ from ._app_environment import AppEnvironment
15
+ from ._parameter import Parameter
16
+
17
+ if typing.TYPE_CHECKING:
18
+ from flyte._deployer import DeployedEnvironment
19
+ from flyte.remote import App
20
+
21
+ FILES_TAR_FILE_NAME = "code_bundle.tgz"
22
+
23
+
24
+ @dataclass
25
+ class DeployedAppEnvironment:
26
+ env: AppEnvironment
27
+ deployed_app: "App"
28
+
29
+ def get_name(self) -> str:
30
+ """
31
+ Returns the name of the deployed environment.
32
+ """
33
+ return self.env.name
34
+
35
+ def env_repr(self) -> typing.List[typing.Tuple[str, ...]]:
36
+ return [
37
+ ("environment", self.env.name),
38
+ ("image", self.env.image.uri if isinstance(self.env.image, Image) else self.env.image or ""),
39
+ ]
40
+
41
+ def table_repr(self) -> typing.List[typing.List[typing.Tuple[str, ...]]]:
42
+ from flyteidl2.app import app_definition_pb2
43
+
44
+ return [
45
+ [
46
+ ("type", "App"),
47
+ ("name", f"[link={self.deployed_app.url}]{self.deployed_app.name}[/link]"),
48
+ ("revision", str(self.deployed_app.revision)),
49
+ (
50
+ "desired state",
51
+ app_definition_pb2.Spec.DesiredState.Name(self.deployed_app.desired_state),
52
+ ),
53
+ (
54
+ "current state",
55
+ app_definition_pb2.Status.DeploymentStatus.Name(self.deployed_app.deployment_status),
56
+ ),
57
+ (
58
+ "public_url",
59
+ self.deployed_app.endpoint,
60
+ ),
61
+ ],
62
+ ]
63
+
64
+ def summary_repr(self) -> str:
65
+ return f"Deployed App[{self.deployed_app.name}] in environment {self.env.name}"
66
+
67
+
68
+ async def _deploy_app(
69
+ app: AppEnvironment,
70
+ serialization_context: SerializationContext,
71
+ parameter_overrides: list[Parameter] | None = None,
72
+ dryrun: bool = False,
73
+ ) -> "App":
74
+ """
75
+ Deploy the given app.
76
+ """
77
+ import flyte.errors
78
+ from flyte.app._runtime import translate_app_env_to_idl
79
+ from flyte.remote import App
80
+
81
+ is_pkl = serialization_context.code_bundle and serialization_context.code_bundle.pkl
82
+ if app.include and not is_pkl:
83
+ # Only bundle when not pickling. If this is a pkl bundle, assume that
84
+ # the AppEnvironment has a server function that will be used to serve
85
+ # the app. This function should contain all of the code needed to serve the app.
86
+ app_file = Path(app._app_filename)
87
+ app_root_dir = app_file.parent
88
+ _preexisting_code_bundle_files = []
89
+ if serialization_context.code_bundle is not None:
90
+ _preexisting_code_bundle_files = serialization_context.code_bundle.files or []
91
+ files = (*_preexisting_code_bundle_files, *[f for f in app.include if f not in _preexisting_code_bundle_files])
92
+ code_bundle = await build_code_bundle_from_relative_paths(files, from_dir=app_root_dir)
93
+ serialization_context.code_bundle = code_bundle
94
+
95
+ if serialization_context.code_bundle and serialization_context.code_bundle.pkl:
96
+ assert app._server is not None, (
97
+ "Server function is required for pkl code bundles, use the app_env.server() decorator to define the "
98
+ "server function."
99
+ )
100
+
101
+ image_uri = app.image.uri if isinstance(app.image, Image) else app.image
102
+ try:
103
+ app_idl = await translate_app_env_to_idl.aio(
104
+ app, serialization_context, parameter_overrides=parameter_overrides
105
+ )
106
+
107
+ if dryrun:
108
+ return app_idl
109
+ ensure_client()
110
+ msg = f"Deploying app {app.name}, with image {image_uri} version {serialization_context.version}"
111
+ if app_idl.spec.HasField("container") and app_idl.spec.container.args:
112
+ msg += f" with args {app_idl.spec.container.args}"
113
+ logger.info(msg)
114
+
115
+ return await App.create.aio(app_idl)
116
+ except Exception as exc:
117
+ logger.error(f"Failed to deploy app {app.name} with image {image_uri}: {exc}")
118
+ raise flyte.errors.DeploymentError(
119
+ f"Failed to deploy app {app.name} with image {image_uri}, Error: {exc!s}"
120
+ ) from exc
121
+
122
+
123
+ async def _deploy_app_env(context: deployer.DeploymentContext) -> DeployedEnvironment:
124
+ if not isinstance(context.environment, AppEnvironment):
125
+ raise TypeError(f"Expected AppEnvironment, got {type(context.environment)}")
126
+
127
+ app_env = context.environment
128
+ deployed_app = await _deploy_app(app_env, context.serialization_context, dryrun=context.dryrun)
129
+
130
+ return DeployedAppEnvironment(env=app_env, deployed_app=deployed_app)