Skip to content

Commit

Permalink
Add job API to support additional external dir in the custom dir (#2748)
Browse files Browse the repository at this point in the history
* Add job API to support additional external dir in the custom dir.

* changed the behavior to copy external dir contents to job custom folder flat.

---------

Co-authored-by: Sean Yang <[email protected]>
  • Loading branch information
yhwen and SYangster authored Aug 8, 2024
1 parent fd4acdf commit 8ade77a
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 2 deletions.
7 changes: 7 additions & 0 deletions nvflare/job_config/base_app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self) -> None:
self.task_result_filters: [(List[str], Filter)] = []
self.components: Dict[str, object] = {}
self.ext_scripts = []
self.ext_dirs = []

self.handlers: [FLComponent] = []

Expand Down Expand Up @@ -62,6 +63,12 @@ def add_ext_script(self, ext_script: str):

self.ext_scripts.append(ext_script)

def add_ext_dir(self, ext_dir: str):
if not (os.path.isdir(ext_dir) and os.path.exists(ext_dir)):
raise RuntimeError(f"external resource dir: {ext_dir} does not exist")

self.ext_dirs.append(ext_dir)

def _add_task_filter(self, tasks, filter, filters):
if not isinstance(filter, Filter):
raise RuntimeError(f"filter must be type of Filter, but got {filter.__class__}")
Expand Down
15 changes: 13 additions & 2 deletions nvflare/job_config/fed_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os.path
import re
import uuid
from typing import Any, List, Union
Expand Down Expand Up @@ -104,6 +104,14 @@ def add_external_scripts(self, external_scripts: List):
for _script in external_scripts:
self.app.add_ext_script(_script)

def add_external_dir(self, ext_dir: str):
"""Register external folder to include them in custom directory.
Args:
ext_dir: external folder that need to be deployed to the client/server.
"""
self.app.add_ext_dir(ext_dir)


class ExecutorApp(FedApp):
def __init__(self):
Expand Down Expand Up @@ -228,7 +236,10 @@ def to(
f"{target} doesn't have a `Controller` or `Executor`. Deploy one first before adding external script!"
)

self._deploy_map[target].add_external_scripts([obj])
if os.path.isdir(obj):
self._deploy_map[target].add_external_dir(obj)
else:
self._deploy_map[target].add_external_scripts([obj])
else: # handle objects that are not Controller or Executor type
if target not in self._deploy_map:
raise ValueError(
Expand Down
7 changes: 7 additions & 0 deletions nvflare/job_config/fed_job_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from typing import Dict

from nvflare.fuel.utils.class_utils import get_component_init_parameters
from nvflare.job_config.base_app_config import BaseAppConfig
from nvflare.job_config.fed_app_config import FedAppConfig
from nvflare.private.fed.app.fl_conf import FL_PACKAGES
from nvflare.private.fed.app.simulator.simulator_runner import SimulatorRunner
Expand Down Expand Up @@ -162,6 +163,7 @@ def _get_server_app(self, config_dir, custom_dir, fed_app):
outfile.write(json_dump)

self._copy_ext_scripts(custom_dir, fed_app.server_app.ext_scripts)
self._copy_ext_dirs(custom_dir, fed_app.server_app)

def _copy_ext_scripts(self, custom_dir, ext_scripts):
for script in ext_scripts:
Expand All @@ -174,6 +176,10 @@ def _copy_ext_scripts(self, custom_dir, ext_scripts):
module = "".join(relative_script.rsplit(".py", 1)).replace(os.sep, ".")
self._copy_source_file(custom_dir, module, script, dest_file)

def _copy_ext_dirs(self, custom_dir, app_config: BaseAppConfig):
for dir in app_config.ext_dirs:
shutil.copytree(dir, custom_dir, dirs_exist_ok=True)

def _get_relative_script(self, script):
package_path = ""
for path in sys.path:
Expand Down Expand Up @@ -245,6 +251,7 @@ def _get_client_app(self, config_dir, custom_dir, fed_app):
outfile.write(json_dump)

self._copy_ext_scripts(custom_dir, fed_app.client_app.ext_scripts)
self._copy_ext_dirs(custom_dir, fed_app.client_app)

def _get_base_app(self, custom_dir, app, app_config):
app_config["components"] = []
Expand Down

0 comments on commit 8ade77a

Please sign in to comment.