Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.5] Fixed the FOBS lazy loading issue #3122

Merged
merged 3 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions nvflare/fuel/utils/fobs/fobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +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 builtins
import importlib
import inspect
import logging
Expand Down Expand Up @@ -66,15 +67,12 @@ def _get_type_name(cls: Type) -> str:

def _load_class(type_name: str):
try:
parts = type_name.split(".")
if len(parts) == 1:
parts = ["builtins", type_name]

mod = __import__(parts[0])
for comp in parts[1:]:
mod = getattr(mod, comp)

return mod
if "." in type_name:
module_name, class_name = type_name.rsplit(".", 1)
module = importlib.import_module(module_name)
return getattr(module, class_name)
else:
return getattr(builtins, type_name)
except Exception as ex:
raise TypeError(f"Can't load class {type_name}: {ex}")

Expand Down Expand Up @@ -243,7 +241,7 @@ def register_folder(folder: str, package: str):
# classes who are abstract or take extra args in __init__ can't be auto-registered
if issubclass(cls_obj, Decomposer) and not inspect.isabstract(cls_obj) and len(spec.args) == 1:
register(cls_obj)
except (ModuleNotFoundError, RuntimeError) as e:
except (ModuleNotFoundError, RuntimeError, ValueError) as e:
log.debug(
f"Try to import module {decomposers}, but failed: {secure_format_exception(e)}. "
f"Can't use name in config to refer to classes in module: {decomposers}."
Expand Down Expand Up @@ -275,7 +273,7 @@ def register_custom_folder(folder: str):
log.warning(
f"Invalid Decomposer from {module}: can't have argument in Decomposer's constructor"
)
except (ModuleNotFoundError, RuntimeError):
except (ModuleNotFoundError, RuntimeError, ValueError):
pass


Expand Down
9 changes: 3 additions & 6 deletions tests/unit_test/fuel/utils/fobs/fobs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ class TestFobs:
NUMBER = 123456
FLOAT = 123.456
NAME = "FOBS Test"
SET = {4, 5, 6}
NOW = datetime.now()

test_data = {
"str": "Test string",
"number": NUMBER,
"float": FLOAT,
"list": [7, 8, 9],
"set": {4, 5, 6},
"set": SET,
"tuple": ("abc", "xyz"),
"time": NOW,
}
Expand All @@ -44,11 +45,7 @@ def test_builtin(self):
buf = fobs.dumps(TestFobs.test_data)
data = fobs.loads(buf)
assert data["number"] == TestFobs.NUMBER

def test_aliases(self):
buf = fobs.dumps(TestFobs.test_data)
data = fobs.loads(buf)
assert data["number"] == TestFobs.NUMBER
assert data["set"] == TestFobs.SET

def test_unsupported_classes(self):
with pytest.raises(TypeError):
Expand Down
Loading