outerbounds 0.3.183rc0__py3-none-any.whl → 0.3.184__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.
@@ -1,125 +0,0 @@
1
- """
2
- Examples demonstrating how to use the code packager abstraction.
3
-
4
- This file provides usage examples for the code packager classes.
5
- These examples are for documentation purposes and are not meant to be run directly.
6
- """
7
-
8
- import os
9
- import sys
10
- from io import BytesIO
11
- from typing import List, Dict, Any, Callable, Optional
12
-
13
- from .code_packager import CodePackager
14
-
15
-
16
- def basic_usage_example(datastore_type: str = "s3") -> None:
17
- """
18
- Basic usage example with ContentAddressedStore.
19
-
20
- This example shows how to:
21
- 1. Define paths to include in a package
22
- 2. Create a package using CodePackager's default packaging
23
- 3. Store the package using ContentAddressedStore directly
24
- 4. Generate a download command
25
-
26
- Parameters
27
- ----------
28
- datastore_type : str, default "s3"
29
- The type of datastore to use: "s3", "azure", "gs", or "local"
30
- """
31
- # Define the paths to include in the package
32
- paths_to_include = ["./"]
33
-
34
- # Define which file suffixes to include
35
- file_suffixes = [".py", ".md"]
36
-
37
- # Create metadata for the package
38
- metadata = {"example": True, "timestamp": "2023-01-01T00:00:00Z"}
39
-
40
- # Initialize the packager with datastore configuration
41
- packager = CodePackager(
42
- datastore_type=datastore_type,
43
- code_package_prefix="my-custom-prefix", # Optional
44
- )
45
-
46
- # Store the package with packaging parameters
47
- package_url, package_key = packager.store(
48
- paths_to_include=paths_to_include,
49
- file_suffixes=file_suffixes,
50
- metadata=metadata,
51
- )
52
-
53
- # Generate a download command
54
- download_cmd = CodePackager.get_download_cmd(
55
- package_url=package_url,
56
- datastore_type=datastore_type,
57
- target_file="my_package.tar",
58
- )
59
-
60
- # Generate complete package commands for downloading and setup
61
- package_commands = packager.get_package_commands(
62
- code_package_url=package_url,
63
- target_file="my_package.tar",
64
- working_dir="my_app",
65
- )
66
-
67
- # Print some information
68
- print(f"Package URL: {package_url}")
69
- print(f"Package Key: {package_key}")
70
- print(f"Download Command: {download_cmd}")
71
- print(f"Complete package commands: {package_commands}")
72
-
73
-
74
- def usage_with_custom_package_function(datastore_type: str = "s3") -> None:
75
- """
76
- Example of using the CodePackager with a custom package creation function.
77
-
78
- Parameters
79
- ----------
80
- datastore_type : str, default "s3"
81
- The type of datastore to use: "s3", "azure", "gs", or "local"
82
- """
83
-
84
- # Define a custom package function
85
- def create_custom_package():
86
- # This is a simple example - in real use, you might create a more complex package
87
- from io import BytesIO
88
- import tarfile
89
- import time
90
-
91
- buf = BytesIO()
92
- with tarfile.open(fileobj=buf, mode="w:gz") as tar:
93
- # Add a simple file to the tarball
94
- content = b"print('Hello, custom package!')"
95
- info = tarfile.TarInfo(name="hello.py")
96
- info.size = len(content)
97
- info.mtime = int(time.time())
98
- file_object = BytesIO(content)
99
- tar.addfile(info, file_object)
100
-
101
- return bytearray(buf.getvalue())
102
-
103
- # Initialize the packager with datastore configuration
104
- packager = CodePackager(datastore_type=datastore_type)
105
-
106
- # Store the package with the custom package function
107
- package_url, package_key = packager.store(package_create_fn=create_custom_package)
108
-
109
- # Generate a download command
110
- download_cmd = CodePackager.get_download_cmd(
111
- package_url=package_url,
112
- datastore_type=datastore_type,
113
- target_file="custom_package.tar",
114
- )
115
-
116
- # Generate complete package commands
117
- package_commands = packager.get_package_commands(
118
- code_package_url=package_url,
119
- )
120
-
121
- # Print some information
122
- print(f"Package URL: {package_url}")
123
- print(f"Package Key: {package_key}")
124
- print(f"Download Command: {download_cmd}")
125
- print(f"Complete commands: {package_commands}")
@@ -1,264 +0,0 @@
1
-
2
- title: Outerbounds App Configuration Schema
3
- description: |
4
- Schema for defining Outerbounds Apps configuration. This schema is what we will end up using on the CLI/programmatic interface.
5
- How to read this schema:
6
- 1. If the a property has `allow_union`:true then it will allow overrides from the cli.
7
- 2. If a property has `experimental` set to true then a lot its validations may-be skipped and parsing handled somewhere else.
8
- version: 1.0.0
9
- type: object
10
- required:
11
- - name
12
- - port
13
- properties:
14
- name: # Only used in `deploy` command
15
- allow_union: true # Allow overriding name from the CLI.
16
- type: string
17
- description: The name of the app to deploy.
18
- maxLength: 20 # todo: check if we should allow a larger length.
19
- example: "myapp"
20
- port: # Only used in `deploy` command
21
- allow_union: false
22
- type: integer
23
- description: Port where the app is hosted. When deployed this will be port on which we will deploy the app.
24
- minimum: 1
25
- maximum: 65535
26
- example: 8000
27
- tags: # Only used in `deploy` command
28
- allow_union: true
29
- type: array
30
- description: The tags of the app to deploy.
31
- items:
32
- type: object
33
- example:
34
- - foo: bar
35
- - x: y
36
- description: # Only used in `deploy` command
37
- allow_union: true
38
- type: string
39
- description: The description of the app to deploy.
40
- example: "This is a description of my app."
41
- force_upgrade: # Only used in `deploy` command
42
- allow_union: true
43
- type: boolean
44
- description: Whether to force upgrade the app even if it is currently being upgraded.
45
- example: true
46
- app_type: # Only used in `deploy` command
47
- allow_union: true
48
- type: string
49
- description: The User defined type of app to deploy. Its only used for bookkeeping purposes.
50
- example: "MyCustomAgent"
51
- image: # Only used in `deploy` command
52
- allow_union: true # We will overrwite the image if specified on the CLI.
53
- type: string
54
- description: The Docker image to deploy with the App.
55
- secrets: # Used in `run` command
56
- allow_union: true
57
- type: array
58
- description: Outerbounds integrations to attach to the app. You can use the value you set in the `@secrets` decorator in your code.
59
- items:
60
- type: string
61
- example: ["hf-token"]
62
- environment: # Used in `run` command
63
- # Todo: So this part might not be best on the CLI. We should probably have a better way to handle this.
64
- # In simplicity, we can just JSON dump anything that looks like a dict/list/
65
- allow_union: true
66
- type: object
67
- description: Environment variables to deploy with the App.
68
- additionalProperties:
69
- oneOf:
70
- - type: string
71
- - type: number
72
- - type: boolean
73
- - type: object
74
- - type: array # When users give arrays, or objects, we need to JSON dump them. Users need to be aware of this.
75
- example:
76
- DEBUG: true
77
- DATABASE_CONFIG: {"host": "localhost", "port": 5432}
78
- ALLOWED_ORIGINS: ["http://localhost:3000", "https://myapp.com"]
79
- dependencies: # Used in `run` command
80
- allow_union: false
81
- type: object
82
- description: |
83
- The dependencies to attach to the app. Only one of the properties can be specified.
84
- properties:
85
- from_requirements_file:
86
- type: string
87
- description: The path to the requirements.txt file to attach to the app.
88
- example: "requirements.txt"
89
- from_pyproject_toml:
90
- type: string
91
- description: The path to the pyproject.toml file to attach to the app.
92
- example: "pyproject.toml"
93
- python:
94
- type: string
95
- description: |
96
- The Python version to use for the app.
97
- example: "3.10"
98
- pypi:
99
- type: object
100
- description: |
101
- A dictionary of pypi dependencies to attach to the app.
102
- The key is the package name and the value is the version.
103
- example:
104
- numpy: 1.23.0
105
- pandas: ""
106
- conda:
107
- type: object
108
- description: |
109
- A dictionary of pypi dependencies to attach to the app.
110
- The key is the package name and the value is the version.
111
- example:
112
- numpy: 1.23.0
113
- pandas: ""
114
- package:
115
- allow_union: false
116
- type: object
117
- description: |
118
- Configurations associated with packaging the app.
119
- properties:
120
- src_path:
121
- type: string
122
- description: The path to the source code to deploy with the App.
123
- example: "./"
124
- suffixes:
125
- type: array
126
- description: |
127
- A list of suffixes to add to the source code to deploy with the App.
128
- items:
129
- type: string
130
- example: [".py", ".ipynb"]
131
-
132
- commands: # Used in `run` command
133
- allow_union: false
134
- type: array
135
- description: A list of commands to run the app with. Cannot be configured from the CLI. Only used in `run` command.
136
- items:
137
- type: string
138
- example: ["python app.py", "python app.py --foo bar"]
139
- resources: # Only used in `deploy` command
140
- allow_union: true # You can override CPU/Memory/GPU/Storage from the CLI.
141
- type: object
142
- properties:
143
- cpu:
144
- type: string
145
- description: CPU resource request and limit.
146
- example: "500m"
147
- default: "1"
148
- memory:
149
- type: string
150
- description: Memory resource request and limit.
151
- example: "512Mi"
152
- default: "4Gi"
153
- gpu:
154
- type: string
155
- description: GPU resource request and limit.
156
- example: "1"
157
- storage:
158
- type: string
159
- description: Storage resource request and limit.
160
- example: "1Gi"
161
- default: "10Gi"
162
- replicas:
163
- allow_union: true
164
- type: object
165
- description: |
166
- The number of replicas to deploy the app with.
167
- properties:
168
- min:
169
- type: integer
170
- description: The minimum number of replicas to deploy the app with.
171
- example: 1
172
- max:
173
- type: integer
174
- description: The maximum number of replicas to deploy the app with.
175
- example: 10
176
- health_check: # Can be used in `run` command
177
- type: object
178
- # `allow_union` property means that any object in this field will be done a union with the config file if something is provided on commanline.
179
- # If it is set to false, then we should throw an exception if the CLI is trying to override something specified in the config file.
180
- # We will only allow unions in certains options. The rest will not allow any unions and only need to be specified in one place.
181
- allow_union: false
182
- properties:
183
- enabled:
184
- type: boolean
185
- description: Whether to enable health checks.
186
- example: true
187
- default: false
188
- path:
189
- type: string
190
- description: The path for health checks.
191
- example: "/health"
192
- initial_delay_seconds:
193
- type: integer
194
- description: Number of seconds to wait before performing the first health check.
195
- example: 10
196
- period_seconds:
197
- type: integer
198
- description: How often to perform the health check.
199
- example: 30
200
- compute_pools: # Only used in `deploy` command
201
- allow_union: true
202
- type: array
203
- description: |
204
- A list of compute pools to deploy the app to.
205
- items:
206
- type: string
207
- example: ["default", "large"]
208
- auth: # Only used in `deploy` command
209
- allow_union: false
210
- type: object
211
- description: |
212
- Auth related configurations.
213
- properties:
214
- type:
215
- type: string
216
- description: |
217
- The type of authentication to use for the app.
218
- enum: [API, Browser]
219
- public:
220
- type: boolean
221
- description: |
222
- Whether the app is public or not.
223
- default: true
224
- # There is an allowed perimeters property
225
- # But that needs a little more thought on how
226
- # to expose.
227
-
228
- # ------------------------------------ EXPERIMENTAL ------------------------------------
229
- project:
230
- type: string
231
- description: The project name to deploy the app to.
232
- experimental: true
233
- allow_union: true
234
- branch:
235
- type: string
236
- description: The branch name to deploy the app to.
237
- experimental: true
238
- allow_union: true
239
-
240
- models: #
241
- type: array
242
- description: model asset ids to include with the deployment. NO CLI Option for this Now.
243
- experimental: true
244
- allow_union: true
245
- items:
246
- type: object
247
- properties:
248
- asset_id:
249
- type: string
250
- asset_instance_id:
251
- type: string
252
- data: #
253
- type: array
254
- description: data asset ids to include with the deployment.
255
- experimental: true
256
- allow_union: true
257
- items:
258
- type: object
259
- properties:
260
- asset_id:
261
- type: string
262
- asset_instance_id:
263
- type: string
264
- # ------------------------------------ EXPERIMENTAL ------------------------------------
@@ -1,115 +0,0 @@
1
- import copy
2
- import json
3
- import os
4
- import shutil
5
- import sys
6
- import tempfile
7
- from hashlib import sha256
8
- from typing import List, Optional, Callable, Any
9
- from .app_config import AppConfig
10
- from .utils import TODOException
11
- from metaflow.metaflow_config import (
12
- get_pinned_conda_libs,
13
- DEFAULT_DATASTORE,
14
- KUBERNETES_CONTAINER_IMAGE,
15
- )
16
- from collections import namedtuple
17
-
18
- BakingStatus = namedtuple(
19
- "BakingStatus", ["image_should_be_baked", "python_path", "resolved_image"]
20
- )
21
-
22
-
23
- class ImageBakingException(Exception):
24
- pass
25
-
26
-
27
- def _safe_open_file(path: str):
28
- if not os.path.exists(path):
29
- raise ImageBakingException(f"File does not exist: {path}")
30
- try:
31
- with open(path, "r") as f:
32
- return f.read()
33
- except Exception as e:
34
- raise ImageBakingException(f"Failed to open file: {e}")
35
-
36
-
37
- def bake_deployment_image(
38
- app_config: AppConfig,
39
- cache_file_path: str,
40
- logger: Optional[Callable[[str], Any]] = None,
41
- ) -> BakingStatus:
42
- # When do we bake an image?
43
- # 1. When the user has specified something like `pypi`/`conda`
44
- # 2, When the user has specified something like `from_requirements`/ `from_pyproject`
45
- # TODO: add parsers for the pyproject/requirements stuff.
46
- from metaflow.ob_internal import bake_image # type: ignore
47
- from metaflow.plugins.pypi.parsers import (
48
- requirements_txt_parser,
49
- pyproject_toml_parser,
50
- )
51
-
52
- image = app_config.get("image", KUBERNETES_CONTAINER_IMAGE)
53
- python_version = "%d.%d.%d" % sys.version_info[:3]
54
-
55
- dependencies = app_config.get_state("dependencies", {})
56
- pypi_packages = {}
57
- conda_packages = {}
58
-
59
- parsed_packages = {}
60
-
61
- if dependencies.get("from_requirements_file"):
62
- parsed_packages = requirements_txt_parser(
63
- _safe_open_file(dependencies.get("from_requirements_file"))
64
- )
65
- pypi_packages = parsed_packages.get("packages", {})
66
- python_version = parsed_packages.get("python_version", python_version)
67
-
68
- elif dependencies.get("from_pyproject_toml"):
69
- parsed_packages = pyproject_toml_parser(
70
- _safe_open_file(dependencies.get("from_pyproject_toml"))
71
- )
72
- pypi_packages = parsed_packages.get("packages", {})
73
- python_version = parsed_packages.get("python_version", python_version)
74
-
75
- elif "pypi" in dependencies:
76
- pypi_packages = dependencies.get("pypi", {})
77
-
78
- if "conda" in dependencies:
79
- conda_packages = dependencies.get("conda", {})
80
- if "python" in dependencies:
81
- python_version = dependencies.get("python", python_version)
82
-
83
- python_packages_exist = len(pypi_packages) > 0 or len(conda_packages) > 0
84
- if (not python_packages_exist) or app_config.get_state("skip_dependencies", False):
85
- # Inform the user that no dependencies are being used.
86
- if app_config.get_state("skip_dependencies", False) and logger:
87
- logger(
88
- "⏭️ Skipping baking dependencies into the image based on the --no-deps flag."
89
- )
90
- # TODO: Handle this a little more nicely.
91
- return BakingStatus(
92
- image_should_be_baked=False, resolved_image=image, python_path="python"
93
- )
94
-
95
- pinned_conda_libs = get_pinned_conda_libs(python_version, DEFAULT_DATASTORE)
96
- pypi_packages.update(pinned_conda_libs)
97
- _reference = app_config.get("name", "default")
98
- # `image` cannot be None. If by change it is none, FB will fart.
99
- fb_response = bake_image(
100
- cache_file_path=cache_file_path,
101
- pypi_packages=pypi_packages,
102
- conda_packages=conda_packages,
103
- ref=_reference,
104
- python=python_version,
105
- base_image=image,
106
- logger=logger,
107
- )
108
- if fb_response.failure:
109
- raise ImageBakingException(f"Failed to bake image: {fb_response.response}")
110
-
111
- return BakingStatus(
112
- image_should_be_baked=True,
113
- resolved_image=fb_response.container_image,
114
- python_path=fb_response.python_path,
115
- )
File without changes
@@ -1,110 +0,0 @@
1
- from functools import wraps
2
- from ..click_importer import click
3
- import os
4
- from typing import TYPE_CHECKING
5
-
6
- if TYPE_CHECKING:
7
- from ..app_config import AppConfig
8
-
9
- DEFAULT_BRANCH = "test"
10
-
11
-
12
- def wrapping_cli_options(func):
13
- @click.option(
14
- "--project",
15
- type=str,
16
- help="The flow project the app/endpoint belongs to",
17
- default=None,
18
- )
19
- @click.option(
20
- "--branch",
21
- type=str,
22
- help="The branch the app/endpoint belongs to",
23
- default=None,
24
- )
25
- @wraps(func)
26
- def wrapper(*args, **kwargs):
27
- return func(*args, **kwargs)
28
-
29
- return wrapper
30
-
31
-
32
- def build_config_from_options(options):
33
- """Build an app configuration from CLI options."""
34
- keys = [
35
- "project",
36
- "branch",
37
- ]
38
- config = {}
39
- for key in keys:
40
- if options.get(key):
41
- config[key] = options.get(key)
42
-
43
- return config
44
-
45
-
46
- # Account for project / branch and the capsule input.
47
- def capsule_input_overrides(app_config: "AppConfig", capsule_input: dict):
48
- project = app_config.get_state("project", None)
49
- # Update the project/branch related configurations.
50
- if project is not None:
51
- branch = app_config.get_state("branch", DEFAULT_BRANCH)
52
- capsule_input["tags"].extend(
53
- [dict(key="project", value=project), dict(key="branch", value=branch)]
54
- )
55
-
56
- model_asset_conf = app_config.get_state("models", None)
57
- data_asset_conf = app_config.get_state("data", None)
58
- code_info = _code_info(app_config)
59
- # todo:fix me
60
- _objects_key = "associatedObjects"
61
- if model_asset_conf or data_asset_conf or code_info:
62
- capsule_input[_objects_key] = {}
63
-
64
- if model_asset_conf:
65
- capsule_input[_objects_key]["models"] = [
66
- {"assetId": x["asset_id"], "assetInstanceId": x["asset_instance_id"]}
67
- for x in model_asset_conf
68
- ]
69
- if data_asset_conf:
70
- capsule_input[_objects_key]["data"] = [
71
- {"assetId": x["asset_id"], "assetInstanceId": x["asset_instance_id"]}
72
- for x in data_asset_conf
73
- ]
74
- if code_info:
75
- capsule_input[_objects_key]["code"] = code_info
76
-
77
- return capsule_input
78
-
79
-
80
- def _code_info(app_config: "AppConfig"):
81
- from metaflow.metaflow_git import get_repository_info
82
-
83
- try:
84
- from metaflow.metaflow_git import _call_git # type: ignore
85
- except ImportError:
86
- # Fallback if _call_git is not available
87
- def _call_git(args, path=None):
88
- return "", 1, True
89
-
90
- repo_info = get_repository_info(app_config.get_state("packaging_directory", None))
91
- if len(repo_info) == 0:
92
- return None
93
-
94
- git_log_info, returncode, failed = _call_git(
95
- ["log", "-1", "--pretty=%B"],
96
- path=app_config.get_state("packaging_directory", None),
97
- )
98
- repo_url = repo_info["repo_url"]
99
- if isinstance(repo_url, str):
100
- _url = repo_url if not repo_url.endswith(".git") else repo_url.rstrip(".git")
101
- else:
102
- _url = str(repo_url)
103
- _code_info = {
104
- "commitId": repo_info["commit_sha"],
105
- "commitLink": os.path.join(_url, "commit", str(repo_info["commit_sha"])),
106
- }
107
- if not failed and returncode == 0 and isinstance(git_log_info, str):
108
- _code_info["commitMessage"] = git_log_info.strip()
109
-
110
- return _code_info
@@ -1,45 +0,0 @@
1
- import os
2
- import json
3
- from typing import Tuple, Union
4
-
5
-
6
- class PerimeterExtractor:
7
- @classmethod
8
- def for_ob_cli(
9
- cls, config_dir: str, profile: str
10
- ) -> Union[Tuple[str, str], Tuple[None, None]]:
11
- """
12
- This function will be called when we are trying to extract the perimeter
13
- via the ob cli's execution. We will rely on the following logic:
14
- 1. check environment variables like OB_CURRENT_PERIMETER / OBP_PERIMETER
15
- 2. run init config to extract the perimeter related configurations.
16
-
17
- Returns
18
- -------
19
- Tuple[str, str] : Tuple containing perimeter name , API server url.
20
- """
21
- from outerbounds.utils import metaflowconfig
22
-
23
- perimeter = None
24
- api_server = None
25
- if os.environ.get("OB_CURRENT_PERIMETER") or os.environ.get("OBP_PERIMETER"):
26
- perimeter = os.environ.get("OB_CURRENT_PERIMETER") or os.environ.get(
27
- "OBP_PERIMETER"
28
- )
29
-
30
- if os.environ.get("OBP_API_SERVER"):
31
- api_server = os.environ.get("OBP_API_SERVER")
32
-
33
- if perimeter is None or api_server is None:
34
- metaflow_config = metaflowconfig.init_config(config_dir, profile)
35
- perimeter = metaflow_config.get("OBP_PERIMETER")
36
- api_server = metaflowconfig.get_sanitized_url_from_config(
37
- config_dir, profile, "OBP_API_SERVER"
38
- )
39
-
40
- return perimeter, api_server # type: ignore
41
-
42
- @classmethod
43
- def during_metaflow_execution(cls) -> str:
44
- # TODO: implement this
45
- return ""