outerbounds 0.3.176rc6__py3-none-any.whl → 0.3.178__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,259 +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
- app_type: # Only used in `deploy` command
42
- allow_union: true
43
- type: string
44
- description: The User defined type of app to deploy. Its only used for bookkeeping purposes.
45
- example: "MyCustomAgent"
46
- image: # Only used in `deploy` command
47
- allow_union: true # We will overrwite the image if specified on the CLI.
48
- type: string
49
- description: The Docker image to deploy with the App.
50
- secrets: # Used in `run` command
51
- allow_union: true
52
- type: array
53
- description: Outerbounds integrations to attach to the app. You can use the value you set in the `@secrets` decorator in your code.
54
- items:
55
- type: string
56
- example: ["hf-token"]
57
- environment: # Used in `run` command
58
- # Todo: So this part might not be best on the CLI. We should probably have a better way to handle this.
59
- # In simplicity, we can just JSON dump anything that looks like a dict/list/
60
- allow_union: true
61
- type: object
62
- description: Environment variables to deploy with the App.
63
- additionalProperties:
64
- oneOf:
65
- - type: string
66
- - type: number
67
- - type: boolean
68
- - type: object
69
- - type: array # When users give arrays, or objects, we need to JSON dump them. Users need to be aware of this.
70
- example:
71
- DEBUG: true
72
- DATABASE_CONFIG: {"host": "localhost", "port": 5432}
73
- ALLOWED_ORIGINS: ["http://localhost:3000", "https://myapp.com"]
74
- dependencies: # Used in `run` command
75
- allow_union: false
76
- type: object
77
- description: |
78
- The dependencies to attach to the app. Only one of the properties can be specified.
79
- properties:
80
- from_requirements_file:
81
- type: string
82
- description: The path to the requirements.txt file to attach to the app.
83
- example: "requirements.txt"
84
- from_pyproject_toml:
85
- type: string
86
- description: The path to the pyproject.toml file to attach to the app.
87
- example: "pyproject.toml"
88
- python:
89
- type: string
90
- description: |
91
- The Python version to use for the app.
92
- example: "3.10"
93
- pypi:
94
- type: object
95
- description: |
96
- A dictionary of pypi dependencies to attach to the app.
97
- The key is the package name and the value is the version.
98
- example:
99
- numpy: 1.23.0
100
- pandas: ""
101
- conda:
102
- type: object
103
- description: |
104
- A dictionary of pypi dependencies to attach to the app.
105
- The key is the package name and the value is the version.
106
- example:
107
- numpy: 1.23.0
108
- pandas: ""
109
- package:
110
- allow_union: false
111
- type: object
112
- description: |
113
- Configurations associated with packaging the app.
114
- properties:
115
- src_path:
116
- type: string
117
- description: The path to the source code to deploy with the App.
118
- example: "./"
119
- suffixes:
120
- type: array
121
- description: |
122
- A list of suffixes to add to the source code to deploy with the App.
123
- items:
124
- type: string
125
- example: [".py", ".ipynb"]
126
-
127
- commands: # Used in `run` command
128
- allow_union: false
129
- type: array
130
- description: A list of commands to run the app with. Cannot be configured from the CLI. Only used in `run` command.
131
- items:
132
- type: string
133
- example: ["python app.py", "python app.py --foo bar"]
134
- resources: # Only used in `deploy` command
135
- allow_union: true # You can override CPU/Memory/GPU/Storage from the CLI.
136
- type: object
137
- properties:
138
- cpu:
139
- type: string
140
- description: CPU resource request and limit.
141
- example: "500m"
142
- default: "1"
143
- memory:
144
- type: string
145
- description: Memory resource request and limit.
146
- example: "512Mi"
147
- default: "4Gi"
148
- gpu:
149
- type: string
150
- description: GPU resource request and limit.
151
- example: "1"
152
- storage:
153
- type: string
154
- description: Storage resource request and limit.
155
- example: "1Gi"
156
- default: "10Gi"
157
- replicas:
158
- allow_union: true
159
- type: object
160
- description: |
161
- The number of replicas to deploy the app with.
162
- properties:
163
- min:
164
- type: integer
165
- description: The minimum number of replicas to deploy the app with.
166
- example: 1
167
- max:
168
- type: integer
169
- description: The maximum number of replicas to deploy the app with.
170
- example: 10
171
- health_check: # Can be used in `run` command
172
- type: object
173
- # `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.
174
- # 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.
175
- # We will only allow unions in certains options. The rest will not allow any unions and only need to be specified in one place.
176
- allow_union: false
177
- properties:
178
- enabled:
179
- type: boolean
180
- description: Whether to enable health checks.
181
- example: true
182
- default: false
183
- path:
184
- type: string
185
- description: The path for health checks.
186
- example: "/health"
187
- initial_delay_seconds:
188
- type: integer
189
- description: Number of seconds to wait before performing the first health check.
190
- example: 10
191
- period_seconds:
192
- type: integer
193
- description: How often to perform the health check.
194
- example: 30
195
- compute_pools: # Only used in `deploy` command
196
- allow_union: true
197
- type: array
198
- description: |
199
- A list of compute pools to deploy the app to.
200
- items:
201
- type: string
202
- example: ["default", "large"]
203
- auth: # Only used in `deploy` command
204
- allow_union: false
205
- type: object
206
- description: |
207
- Auth related configurations.
208
- properties:
209
- type:
210
- type: string
211
- description: |
212
- The type of authentication to use for the app.
213
- enum: [API, Browser]
214
- public:
215
- type: boolean
216
- description: |
217
- Whether the app is public or not.
218
- default: true
219
- # There is an allowed perimeters property
220
- # But that needs a little more thought on how
221
- # to expose.
222
-
223
- # ------------------------------------ EXPERIMENTAL ------------------------------------
224
- project:
225
- type: string
226
- description: The project name to deploy the app to.
227
- experimental: true
228
- allow-union: true
229
- branch:
230
- type: string
231
- description: The branch name to deploy the app to.
232
- experimental: true
233
- allow-union: true
234
-
235
- models: #
236
- type: array
237
- description: model asset ids to include with the deployment. NO CLI Option for this Now.
238
- experimental: true
239
- allow-union: true
240
- items:
241
- type: object
242
- properties:
243
- asset_id:
244
- type: string
245
- asset_instance_id:
246
- type: string
247
- data: #
248
- type: array
249
- description: data asset ids to include with the deployment.
250
- experimental: true
251
- allow-union: true
252
- items:
253
- type: object
254
- properties:
255
- asset_id:
256
- type: string
257
- asset_instance_id:
258
- type: string
259
- # ------------------------------------ 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 TODOException(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 TODOException(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
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):
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,103 +0,0 @@
1
- from functools import wraps
2
- from outerbounds._vendor 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, _call_git
82
-
83
- repo_info = get_repository_info(app_config.get_state("packaging_directory", None))
84
- if len(repo_info) == 0:
85
- return None
86
-
87
- git_log_info, returncode, failed = _call_git(
88
- ["log", "-1", "--pretty=%B"],
89
- path=app_config.get_state("packaging_directory", None),
90
- )
91
- _url = (
92
- repo_info["repo_url"]
93
- if not repo_info["repo_url"].endswith(".git")
94
- else repo_info["repo_url"].rstrip(".git")
95
- )
96
- _code_info = {
97
- "commitId": repo_info["commit_sha"],
98
- "commitLink": os.path.join(_url, "commit", repo_info["commit_sha"]),
99
- }
100
- if not failed and returncode == 0:
101
- _code_info["commitMessage"] = git_log_info.strip()
102
-
103
- return _code_info