ob-metaflow-extensions 1.1.163rc6__py2.py3-none-any.whl → 1.1.164__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.

@@ -41,6 +41,9 @@ def get_boto3_session(role_arn=None, session_vars=None):
41
41
  import boto3
42
42
  import botocore
43
43
  from metaflow_extensions.outerbounds.plugins.auth_server import get_token
44
+ from metaflow_extensions.outerbounds.plugins.aws.assume_role import (
45
+ OBP_ASSUME_ROLE_ARN_ENV_VAR,
46
+ )
44
47
 
45
48
  from hashlib import sha256
46
49
  from metaflow.util import get_username
@@ -49,6 +52,10 @@ def get_boto3_session(role_arn=None, session_vars=None):
49
52
 
50
53
  token_info = get_token("/generate/aws")
51
54
 
55
+ # Check if the assume_role decorator has set a role ARN via environment variable
56
+ # This takes precedence over CSPR role
57
+ decorator_role_arn = os.environ.get(OBP_ASSUME_ROLE_ARN_ENV_VAR)
58
+
52
59
  # Write token to a file. The file name is derived from the user name
53
60
  # so it works with multiple users on the same machine.
54
61
  #
@@ -69,12 +76,13 @@ def get_boto3_session(role_arn=None, session_vars=None):
69
76
  if token_info.get("cspr_role_arn"):
70
77
  cspr_role = token_info["cspr_role_arn"]
71
78
 
72
- if cspr_role:
73
- # If CSPR role is set, we set it as the default role to assume
74
- # for the AWS SDK. We do this by writing an AWS config file
79
+ # If assume_role decorator is used, prioritize it over CSPR role
80
+ effective_role = decorator_role_arn or cspr_role
81
+ if effective_role:
82
+ # If we have either a decorator role or CSPR role, set up AWS config
75
83
  # with two profiles. One to get credentials for the task role
76
84
  # in exchange for the OIDC token, and second to assume the
77
- # CSPR role using the task role credentials.
85
+ # effective role using the task role credentials.
78
86
  import configparser
79
87
  from io import StringIO
80
88
 
@@ -86,9 +94,9 @@ def get_boto3_session(role_arn=None, session_vars=None):
86
94
  "web_identity_token_file": token_file,
87
95
  }
88
96
 
89
- # CSPR role profile (default)
90
- aws_config["profile cspr"] = {
91
- "role_arn": cspr_role,
97
+ # Effective role profile (decorator role or CSPR role)
98
+ aws_config["profile effective"] = {
99
+ "role_arn": effective_role,
92
100
  "source_profile": "task",
93
101
  }
94
102
 
@@ -104,7 +112,7 @@ def get_boto3_session(role_arn=None, session_vars=None):
104
112
  tmp_aws_config_file = f.name
105
113
  os.rename(tmp_aws_config_file, aws_config_file)
106
114
  os.environ["AWS_CONFIG_FILE"] = aws_config_file
107
- os.environ["AWS_PROFILE"] = "cspr"
115
+ os.environ["AWS_PROFILE"] = "effective"
108
116
  else:
109
117
  os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file
110
118
  os.environ["AWS_ROLE_ARN"] = token_info["role_arn"]
@@ -116,21 +124,29 @@ def get_boto3_session(role_arn=None, session_vars=None):
116
124
  if token_info.get("region"):
117
125
  os.environ["AWS_DEFAULT_REGION"] = token_info["region"]
118
126
 
119
- if cspr_role:
120
- # The generated AWS config will be used here since we set the
121
- # AWS_CONFIG_FILE environment variable above.
122
- if role_arn == USE_CSPR_ROLE_ARN_IF_SET:
123
- # Otherwise start from the default profile, assuming CSPR role
124
- session = boto3.session.Session(profile_name="cspr")
127
+ if effective_role:
128
+ if role_arn == decorator_role_arn or role_arn == USE_CSPR_ROLE_ARN_IF_SET:
129
+ # We have either a decorator role or CSPR role, use the effective profile
130
+ # The generated AWS config will be used here since we set the
131
+ # AWS_CONFIG_FILE environment variable above.
132
+ session = boto3.session.Session(profile_name="effective")
125
133
  else:
126
134
  session = boto3.session.Session(profile_name="task")
127
135
  else:
136
+ # No decorator role or CSPR role, use default session
128
137
  # Not using AWS config, just AWS_WEB_IDENTITY_TOKEN_FILE + AWS_ROLE_ARN
129
138
  session = boto3.session.Session()
130
139
 
131
- if role_arn and role_arn != USE_CSPR_ROLE_ARN_IF_SET:
132
- # If the user provided a role_arn, we assume that role
133
- # using the task role credentials. CSPR role is not used.
140
+ if (
141
+ role_arn
142
+ and role_arn != USE_CSPR_ROLE_ARN_IF_SET
143
+ and role_arn != decorator_role_arn
144
+ ):
145
+ # If the user provided a role_arn that's different from the decorator role,
146
+ # we assume that role using the current session credentials.
147
+ # This works for both cases:
148
+ # 1. No decorator role: Task role -> Secrets role
149
+ # 2. With decorator role: Decorator role -> Secrets role
134
150
  fetcher = botocore.credentials.AssumeRoleCredentialFetcher(
135
151
  client_creator=session._session.create_client,
136
152
  source_credentials=session._session.get_credentials(),
@@ -146,8 +162,8 @@ def get_boto3_session(role_arn=None, session_vars=None):
146
162
  else:
147
163
  # If the user didn't provide a role_arn, or if the role_arn
148
164
  # is set to USE_CSPR_ROLE_ARN_IF_SET, we return the default
149
- # session which would use the CSPR role if it is set on the
150
- # server, and the task role otherwise.
165
+ # session which would use the effective role (decorator or CSPR) if set,
166
+ # or the task role otherwise.
151
167
  return session
152
168
 
153
169
 
@@ -0,0 +1,4 @@
1
+ from .assume_role_decorator import assume_role
2
+ from .assume_role import OBP_ASSUME_ROLE_ARN_ENV_VAR
3
+
4
+ __mf_promote_submodules__ = ["plugins.aws"]
@@ -0,0 +1,3 @@
1
+ # Environment variable name used by the assume_role decorator to pass
2
+ # the role ARN to AWS client functions
3
+ OBP_ASSUME_ROLE_ARN_ENV_VAR = "OBP_ASSUME_ROLE_ARN"
@@ -0,0 +1,65 @@
1
+ from metaflow.user_configs.config_decorators import (
2
+ MutableFlow,
3
+ MutableStep,
4
+ CustomFlowDecorator,
5
+ )
6
+ from .assume_role import OBP_ASSUME_ROLE_ARN_ENV_VAR
7
+
8
+
9
+ class assume_role(CustomFlowDecorator):
10
+ """
11
+ Flow-level decorator for assuming AWS IAM roles.
12
+
13
+ When applied to a flow, all steps in the flow will automatically use the specified IAM role-arn
14
+ as their source principal.
15
+
16
+ Usage:
17
+ ------
18
+ @assume_role(role_arn="arn:aws:iam::123456789012:role/my-iam-role")
19
+ class MyFlow(FlowSpec):
20
+ @step
21
+ def start(self):
22
+ import boto3
23
+ client = boto3.client("dynamodb") # Automatically uses the role in the flow decorator
24
+ self.next(self.end)
25
+
26
+ @step
27
+ def end(self):
28
+ from metaflow import get_aws_client
29
+ client = get_aws_client("dynamodb") # Automatically uses the role in the flow decorator
30
+ """
31
+
32
+ def init(self, *args, **kwargs):
33
+ self.role_arn = kwargs.get("role_arn", None)
34
+
35
+ if self.role_arn is None:
36
+ raise ValueError(
37
+ "`role_arn` keyword argument is required for the assume_role decorator"
38
+ )
39
+
40
+ if not self.role_arn.startswith("arn:aws:iam::"):
41
+ raise ValueError(
42
+ "`role_arn` must be a valid AWS IAM role ARN starting with 'arn:aws:iam::'"
43
+ )
44
+
45
+ def evaluate(self, mutable_flow: MutableFlow) -> None:
46
+ """
47
+ This method is called by Metaflow to apply the decorator to the flow.
48
+ It sets up environment variables that will be used by the AWS client
49
+ to automatically assume the specified role.
50
+ """
51
+ # Import environment decorator at runtime to avoid circular imports
52
+ from metaflow import environment
53
+
54
+ # Set the role ARN as an environment variable that will be picked up
55
+ # by the get_aws_client function
56
+ def _setup_role_assumption(step: MutableStep) -> None:
57
+ # We'll inject the role assumption by adding an environment decorator
58
+ # The role will be available through an environment variable
59
+ step.add_decorator(
60
+ environment, vars={OBP_ASSUME_ROLE_ARN_ENV_VAR: self.role_arn}
61
+ )
62
+
63
+ # Apply the role assumption setup to all steps in the flow
64
+ for _, step in mutable_flow.steps:
65
+ _setup_role_assumption(step)
@@ -20,8 +20,19 @@ def get_aws_client(
20
20
  module, with_error=False, role_arn=None, session_vars=None, client_params=None
21
21
  ):
22
22
  import metaflow.plugins.aws.aws_client
23
+ import os
23
24
 
24
25
  from metaflow_extensions.outerbounds.plugins import USE_CSPR_ROLE_ARN_IF_SET
26
+ from metaflow_extensions.outerbounds.plugins.aws.assume_role import (
27
+ OBP_ASSUME_ROLE_ARN_ENV_VAR,
28
+ )
29
+
30
+ # Check if the assume_role decorator has set a role ARN via environment variable
31
+ # This takes precedence over CSPR but not over explicitly passed role_arn
32
+ if role_arn is None:
33
+ decorator_role_arn = os.environ.get(OBP_ASSUME_ROLE_ARN_ENV_VAR)
34
+ if decorator_role_arn:
35
+ role_arn = decorator_role_arn
25
36
 
26
37
  return metaflow.plugins.aws.aws_client.get_aws_client(
27
38
  module,
@@ -42,10 +53,20 @@ def get_aws_client(
42
53
  def S3(*args, **kwargs):
43
54
  import sys
44
55
  import metaflow.plugins.datatools.s3
56
+ import os
45
57
  from metaflow_extensions.outerbounds.plugins import USE_CSPR_ROLE_ARN_IF_SET
58
+ from metaflow_extensions.outerbounds.plugins.aws.assume_role import (
59
+ OBP_ASSUME_ROLE_ARN_ENV_VAR,
60
+ )
46
61
 
62
+ # Check if the assume_role decorator has set a role ARN via environment variable
63
+ # This takes precedence over CSPR but not over explicitly passed role
47
64
  if "role" not in kwargs or kwargs["role"] is None:
48
- kwargs["role"] = USE_CSPR_ROLE_ARN_IF_SET
65
+ decorator_role_arn = os.environ.get(OBP_ASSUME_ROLE_ARN_ENV_VAR)
66
+ if decorator_role_arn:
67
+ kwargs["role"] = decorator_role_arn
68
+ else:
69
+ kwargs["role"] = USE_CSPR_ROLE_ARN_IF_SET
49
70
 
50
71
  return metaflow.plugins.datatools.s3.S3(*args, **kwargs)
51
72
 
@@ -53,4 +74,4 @@ def S3(*args, **kwargs):
53
74
  from .. import profilers
54
75
  from ..plugins.snowflake import Snowflake
55
76
  from ..plugins.checkpoint_datastores import nebius_checkpoints, coreweave_checkpoints
56
- from . import ob_internal
77
+ from ..plugins.aws import assume_role
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow-extensions
3
- Version: 1.1.163rc6
3
+ Version: 1.1.164
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
@@ -1,7 +1,7 @@
1
1
  metaflow_extensions/outerbounds/__init__.py,sha256=Gb8u06s9ClQsA_vzxmkCzuMnigPy7kKcDnLfb7eB-64,514
2
2
  metaflow_extensions/outerbounds/remote_config.py,sha256=pEFJuKDYs98eoB_-ryPjVi9b_c4gpHMdBHE14ltoxIU,4672
3
3
  metaflow_extensions/outerbounds/config/__init__.py,sha256=JsQGRuGFz28fQWjUvxUgR8EKBLGRdLUIk_buPLJplJY,1225
4
- metaflow_extensions/outerbounds/plugins/__init__.py,sha256=GxYKjrMJCGVKoxhfdPAlVF9kYrEb3-xn9fgUTb_H9VY,13255
4
+ metaflow_extensions/outerbounds/plugins/__init__.py,sha256=QERuUPciMFDxUfzZThqArCGVI_lRXSAASUnQVOmlw8E,14058
5
5
  metaflow_extensions/outerbounds/plugins/auth_server.py,sha256=_Q9_2EL0Xy77bCRphkwT1aSu8gQXRDOH-Z-RxTUO8N4,2202
6
6
  metaflow_extensions/outerbounds/plugins/perimeters.py,sha256=QXh3SFP7GQbS-RAIxUOPbhPzQ7KDFVxZkTdKqFKgXjI,2697
7
7
  metaflow_extensions/outerbounds/plugins/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -9,6 +9,9 @@ metaflow_extensions/outerbounds/plugins/apps/app_utils.py,sha256=sw9whU17lAzlD2K
9
9
  metaflow_extensions/outerbounds/plugins/apps/consts.py,sha256=iHsyqbUg9k-rgswCs1Jxf5QZIxR1V-peCDRjgr9kdBM,177
10
10
  metaflow_extensions/outerbounds/plugins/apps/deploy_decorator.py,sha256=VkmiMdNYHhNdt-Qm9AVv7aE2LWFsIFEc16YcOYjwF6Q,8568
11
11
  metaflow_extensions/outerbounds/plugins/apps/supervisord_utils.py,sha256=GQoN2gyPClcpR9cLldJmbCfqXnoAHxp8xUnY7vzaYtY,9026
12
+ metaflow_extensions/outerbounds/plugins/aws/__init__.py,sha256=VBGdjNKeFLXGZuqh4jVk8cFtO1AWof73a6k_cnbAOYA,145
13
+ metaflow_extensions/outerbounds/plugins/aws/assume_role.py,sha256=mBewNlnSYsR2rFXFkX-DUH6ku01h2yOcMcLHoCL7eyI,161
14
+ metaflow_extensions/outerbounds/plugins/aws/assume_role_decorator.py,sha256=b3te8-RJxsj5cqXbQTFxxeIfnqTERAFInzLgXLVLyYA,2391
12
15
  metaflow_extensions/outerbounds/plugins/card_utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
16
  metaflow_extensions/outerbounds/plugins/card_utilities/async_cards.py,sha256=6rQhtZXK5DenXPfCRS1ul0jvLJYd48jrJAlnodID21w,4347
14
17
  metaflow_extensions/outerbounds/plugins/card_utilities/extra_components.py,sha256=D8rgCaSc7PuLD0MHJjqsjN0g0PQMN1H-ySOJqi5uIOE,19111
@@ -17,7 +20,6 @@ metaflow_extensions/outerbounds/plugins/checkpoint_datastores/__init__.py,sha256
17
20
  metaflow_extensions/outerbounds/plugins/checkpoint_datastores/coreweave.py,sha256=_WzoOROFjoFa8TzsMNFp-r_1Zz7NUp-5ljn_kKlczXA,4534
18
21
  metaflow_extensions/outerbounds/plugins/checkpoint_datastores/nebius.py,sha256=zgqDLFewCeF5jqh-hUNKmC_OAjld09ln0bb8Lkeqapc,4659
19
22
  metaflow_extensions/outerbounds/plugins/fast_bakery/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- metaflow_extensions/outerbounds/plugins/fast_bakery/baker.py,sha256=ShE5omFBr83wkvEhL_ptRFvDNMs6wefg4BjaafQjTcM,3602
21
23
  metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py,sha256=Tl520HdBteg-aDOM7mnnJJpdDCZc49BmFFmLUc_vTi8,15018
22
24
  metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py,sha256=PE81ZB54OAMXkMGSB7JqgvgMg7N9kvoVclrWL-6jc2U,5626
23
25
  metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_cli.py,sha256=kqFyu2bJSnc9_9aYfBpz5xK6L6luWFZK_NMuh8f1eVk,1494
@@ -68,15 +70,14 @@ metaflow_extensions/outerbounds/plugins/torchtune/__init__.py,sha256=TOXNeyhcgd8
68
70
  metaflow_extensions/outerbounds/profilers/__init__.py,sha256=wa_jhnCBr82TBxoS0e8b6_6sLyZX0fdHicuGJZNTqKw,29
69
71
  metaflow_extensions/outerbounds/profilers/gpu.py,sha256=3Er8uKQzfm_082uadg4yn_D4Y-iSCgzUfFmguYxZsz4,27485
70
72
  metaflow_extensions/outerbounds/toplevel/__init__.py,sha256=qWUJSv_r5hXJ7jV_On4nEasKIfUCm6_UjkjXWA_A1Ts,90
71
- metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py,sha256=505fLAY4NkBCGtjpXlE0RgaKVpJ0jOUq1-Fq-EKzPew,2035
72
- metaflow_extensions/outerbounds/toplevel/ob_internal.py,sha256=53xM6d_UYT3uGFFA59UzxN23H5QMO5_F39pALpmGy04,51
73
+ metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py,sha256=FS0ZKQJsJKlw9PgtLqVV4kH9o7_qwYcIVtyu2Kqwa_U,2973
73
74
  metaflow_extensions/outerbounds/toplevel/plugins/azure/__init__.py,sha256=WUuhz2YQfI4fz7nIcipwwWq781eaoHEk7n4GAn1npDg,63
74
75
  metaflow_extensions/outerbounds/toplevel/plugins/gcp/__init__.py,sha256=BbZiaH3uILlEZ6ntBLKeNyqn3If8nIXZFq_Apd7Dhco,70
75
76
  metaflow_extensions/outerbounds/toplevel/plugins/kubernetes/__init__.py,sha256=5zG8gShSj8m7rgF4xgWBZFuY3GDP5n1T0ktjRpGJLHA,69
76
77
  metaflow_extensions/outerbounds/toplevel/plugins/ollama/__init__.py,sha256=GRSz2zwqkvlmFS6bcfYD_CX6CMko9DHQokMaH1iBshA,47
77
78
  metaflow_extensions/outerbounds/toplevel/plugins/snowflake/__init__.py,sha256=LptpH-ziXHrednMYUjIaosS1SXD3sOtF_9_eRqd8SJw,50
78
79
  metaflow_extensions/outerbounds/toplevel/plugins/torchtune/__init__.py,sha256=uTVkdSk3xZ7hEKYfdlyVteWj5KeDwaM1hU9WT-_YKfI,50
79
- ob_metaflow_extensions-1.1.163rc6.dist-info/METADATA,sha256=IGm75le9ACGIrfLPPPMKmqKsDrhkoAex5xZ7KDikMr4,524
80
- ob_metaflow_extensions-1.1.163rc6.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
81
- ob_metaflow_extensions-1.1.163rc6.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
82
- ob_metaflow_extensions-1.1.163rc6.dist-info/RECORD,,
80
+ ob_metaflow_extensions-1.1.164.dist-info/METADATA,sha256=SEMbmxC4QBlLcwZQ8Wr9YKxoi6dl6KJoHRtau7Zsx4k,521
81
+ ob_metaflow_extensions-1.1.164.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
82
+ ob_metaflow_extensions-1.1.164.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
83
+ ob_metaflow_extensions-1.1.164.dist-info/RECORD,,
@@ -1,110 +0,0 @@
1
- import threading
2
- import time
3
- import sys
4
- from typing import Dict, Optional, Any, Callable
5
- from functools import partial
6
- from metaflow.exception import MetaflowException
7
- from metaflow.metaflow_config import FAST_BAKERY_URL
8
-
9
- from .fast_bakery import FastBakery, FastBakeryApiResponse, FastBakeryException
10
- from .docker_environment import cache_request
11
-
12
- BAKERY_METAFILE = ".imagebakery-cache"
13
-
14
-
15
- class BakerException(MetaflowException):
16
- headline = "Ran into an error while baking image"
17
-
18
- def __init__(self, msg):
19
- super(BakerException, self).__init__(msg)
20
-
21
-
22
- def bake_image(
23
- cache_file_path: str,
24
- ref: Optional[str] = None,
25
- python: Optional[str] = None,
26
- pypi_packages: Optional[Dict[str, str]] = None,
27
- conda_packages: Optional[Dict[str, str]] = None,
28
- base_image: Optional[str] = None,
29
- logger: Optional[Callable[[str], Any]] = None,
30
- ) -> FastBakeryApiResponse:
31
- """
32
- Bakes a Docker image with the specified dependencies.
33
-
34
- Args:
35
- cache_file_path: Path to the cache file
36
- ref: Reference identifier for this bake (for logging purposes)
37
- python: Python version to use
38
- pypi_packages: Dictionary of PyPI packages and versions
39
- conda_packages: Dictionary of Conda packages and versions
40
- base_image: Base Docker image to use
41
- logger: Optional logger function to output progress
42
-
43
- Returns:
44
- FastBakeryApiResponse: The response from the bakery service
45
-
46
- Raises:
47
- BakerException: If the baking process fails
48
- """
49
- # Default logger if none provided
50
- if logger is None:
51
- logger = partial(print, file=sys.stderr)
52
-
53
- # Thread lock for logging
54
- logger_lock = threading.Lock()
55
- images_baked = 0
56
-
57
- @cache_request(cache_file_path)
58
- def _cached_bake(
59
- ref=None,
60
- python=None,
61
- pypi_packages=None,
62
- conda_packages=None,
63
- base_image=None,
64
- ):
65
- try:
66
- bakery = FastBakery(url=FAST_BAKERY_URL)
67
- bakery._reset_payload()
68
- bakery.python_version(python)
69
- bakery.pypi_packages(pypi_packages)
70
- bakery.conda_packages(conda_packages)
71
- bakery.base_image(base_image)
72
- # bakery.ignore_cache()
73
-
74
- with logger_lock:
75
- logger(f"🍳 Baking [{ref}] ...")
76
- logger(f" 🐍 Python: {python}")
77
-
78
- if pypi_packages:
79
- logger(f" 📦 PyPI packages:")
80
- for package, version in pypi_packages.items():
81
- logger(f" 🔧 {package}: {version}")
82
-
83
- if conda_packages:
84
- logger(f" 📦 Conda packages:")
85
- for package, version in conda_packages.items():
86
- logger(f" 🔧 {package}: {version}")
87
-
88
- logger(f" 🏗️ Base image: {base_image}")
89
-
90
- start_time = time.time()
91
- res = bakery.bake()
92
- # TODO: Get actual bake time from bakery
93
- bake_time = time.time() - start_time
94
-
95
- with logger_lock:
96
- logger(f"🏁 Baked [{ref}] in {bake_time:.2f} seconds!")
97
- nonlocal images_baked
98
- images_baked += 1
99
- return res
100
- except FastBakeryException as ex:
101
- raise BakerException(f"Bake [{ref}] failed: {str(ex)}")
102
-
103
- # Call the cached bake function with the provided parameters
104
- return _cached_bake(
105
- ref=ref,
106
- python=python,
107
- pypi_packages=pypi_packages,
108
- conda_packages=conda_packages,
109
- base_image=base_image,
110
- )
@@ -1 +0,0 @@
1
- from ..plugins.fast_bakery.baker import bake_image