Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6ed7c43

Browse files
committed
Support multiple local dirs for zip files
1 parent cabbc22 commit 6ed7c43

File tree

1 file changed

+56
-20
lines changed

1 file changed

+56
-20
lines changed

launch/client.py

+56-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import shutil
55
import tempfile
66
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
7+
from zipfile import Zipfile
78

89
import cloudpickle
910
import requests
@@ -137,23 +138,55 @@ def register_endpoint_auth_decorator(self, endpoint_auth_decorator_fn):
137138
"""
138139
self.endpoint_auth_decorator_fn = endpoint_auth_decorator_fn
139140

140-
def create_model_bundle_from_dir(
141+
def create_model_bundle_from_dirs(
141142
self,
142143
model_bundle_name: str,
143-
base_path: str,
144+
base_paths: List[str],
144145
requirements_path: str,
145146
env_params: Dict[str, str],
146147
load_predict_fn_module_path: str,
147148
load_model_fn_module_path: str,
148149
app_config: Optional[Union[Dict[str, Any], str]] = None,
149150
) -> ModelBundle:
150151
"""
151-
Packages up code from a local filesystem folder and uploads that as a bundle to Scale Launch.
152+
Packages up code from one or more local filesystem folders and uploads them as a bundle to Scale Launch.
152153
In this mode, a bundle is just local code instead of a serialized object.
153154
155+
For example, if you have a directory structure like so, and your current working directory is also `my_root`:
156+
157+
```
158+
my_root/
159+
my_module1/
160+
__init__.py
161+
...files and directories
162+
my_inference_file.py
163+
my_module2/
164+
__init__.py
165+
...files and directories
166+
```
167+
168+
then calling `create_model_bundle_from_dirs` with `base_paths=["my_module1", "my_module2"]` essentially
169+
creates a zip file without the root directory, e.g.:
170+
171+
```
172+
my_module1/
173+
__init__.py
174+
...files and directories
175+
my_inference_file.py
176+
my_module2/
177+
__init__.py
178+
...files and directories
179+
```
180+
181+
and these contents will be unzipped relative to the server side `PYTHONPATH`. Bear these points in mind when
182+
referencing Python module paths for this bundle. For instance, if `my_inference_file.py` has `def f(...)`
183+
as the desired inference loading function, then the `load_predict_fn_module_path` argument should be
184+
`my_module1.my_inference_file.f`.
185+
186+
154187
Parameters:
155188
model_bundle_name: Name of model bundle you want to create. This acts as a unique identifier.
156-
base_path: The path on the local filesystem where the bundle code lives.
189+
base_paths: The paths on the local filesystem where the bundle code lives.
157190
requirements_path: A path on the local filesystem where a requirements.txt file lives.
158191
env_params: A dictionary that dictates environment information e.g.
159192
the use of pytorch or tensorflow, which cuda/cudnn versions to use.
@@ -163,9 +196,9 @@ def create_model_bundle_from_dir(
163196
"cuda_version": Version of cuda used, e.g. "11.0".
164197
"cudnn_version" Version of cudnn used, e.g. "cudnn8-devel".
165198
"tensorflow_version": Version of tensorflow, e.g. "2.3.0". Only applicable if framework_type is tensorflow
166-
load_predict_fn_module_path: A python module path within base_path for a function that, when called with the output of
199+
load_predict_fn_module_path: A python module path for a function that, when called with the output of
167200
load_model_fn_module_path, returns a function that carries out inference.
168-
load_model_fn_module_path: A python module path within base_path for a function that returns a model. The output feeds into
201+
load_model_fn_module_path: A python module path for a function that returns a model. The output feeds into
169202
the function located at load_predict_fn_module_path.
170203
app_config: Either a Dictionary that represents a YAML file contents or a local path to a YAML file.
171204
"""
@@ -174,20 +207,9 @@ def create_model_bundle_from_dir(
174207

175208
tmpdir = tempfile.mkdtemp()
176209
try:
177-
tmparchive = os.path.join(tmpdir, "bundle")
178-
abs_base_path = os.path.abspath(base_path)
179-
root_dir = os.path.dirname(abs_base_path)
180-
base_dir = os.path.basename(abs_base_path)
181-
182-
with open(
183-
shutil.make_archive(
184-
base_name=tmparchive,
185-
format="zip",
186-
root_dir=root_dir,
187-
base_dir=base_dir,
188-
),
189-
"rb",
190-
) as zip_f:
210+
zip_path = os.path.join(tmpdir, "bundle.zip")
211+
_zip_directories(zip_path, base_paths)
212+
with open(zip_path, "rb") as zip_f:
191213
data = zip_f.read()
192214
finally:
193215
shutil.rmtree(tmpdir)
@@ -771,3 +793,17 @@ def get_batch_async_response(self, batch_async_task_id: str):
771793
TODO Something similar to a list of signed s3URLs
772794
"""
773795
raise NotImplementedError
796+
797+
798+
def _zip_directory(zipf: zipfile.Zipfile, path: str) -> None:
799+
for root, _, files in os.walk(path):
800+
for file in files:
801+
zipf.write(filename=os.path.join(root, file),
802+
arcname=os.path.relpath(os.path.join(root, file),
803+
os.path.join(path, '..')))
804+
805+
806+
def _zip_directories(zip_path: str, dir_list: List[str]) -> None:
807+
with zipfile.ZipFile(zip_path, 'w') as zip_f:
808+
for dir in dir_list:
809+
zip_directory(zipf, dir)

0 commit comments

Comments
 (0)