Repository: NVIDIA/NVFlare

Issue:
if/elif statement without else clause in `FullModelShareableGenerator`
It would be helpful to add an else statement with a warning message that this DataKind is not supported. I ran into this issue when sending a DataKind.COLLECTION with the shareable by mistake.

See https://github.com/NVIDIA/NVFlare/blob/b3ff7844a9bef746218527ccd07601feb66fd94c/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py#L61

In the same class, when sending a DXO instead of Shareable type, I got this error

```
Traceback (most recent call last):
  File "/home/hroth/Code/nvflare/hroth-agglib/nvflare/app_common/workflows/scatter_and_gather.py", line 202, in control_flow
    self._global_weights = self.shareable_gen.shareable_to_learnable(aggr_result, fl_ctx)
  File "/home/hroth/Code/nvflare/hroth-agglib/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py", line 54, in shareable_to_learnable
    dxo = from_shareable(shareable)
  File "/home/hroth/Code/nvflare/hroth-agglib/nvflare/apis/dxo.py", line 120, in from_shareable
    content_type = s.get_header(ReservedHeaderKey.CONTENT_TYPE)
AttributeError: 'DXO' object has no attribute 'get_header'
```
There should be an instance check here https://github.com/NVIDIA/NVFlare/blob/b3ff7844a9bef746218527ccd07601feb66fd94c/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py#L54





Discussion:

Resolving patch:
diff --git a/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py b/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py
--- a/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py
+++ b/nvflare/app_common/shareablegenerators/full_model_shareable_generator.py
@@ -21,21 +21,21 @@
 
 
 class FullModelShareableGenerator(ShareableGenerator):
-    def learnable_to_shareable(self, ml: ModelLearnable, fl_ctx: FLContext) -> Shareable:
-        """Convert Learnable to Shareable.
+    def learnable_to_shareable(self, model_learnable: ModelLearnable, fl_ctx: FLContext) -> Shareable:
+        """Convert ModelLearnable to Shareable.
 
         Args:
-            model (Learnable): model to be converted
+            model_learnable (ModelLearnable): model to be converted
             fl_ctx (FLContext): FL context
 
         Returns:
-            Shareable: a shareable containing a DXO object,
+            Shareable: a shareable containing a DXO object.
         """
-        dxo = model_learnable_to_dxo(ml)
+        dxo = model_learnable_to_dxo(model_learnable)
         return dxo.to_shareable()
 
     def shareable_to_learnable(self, shareable: Shareable, fl_ctx: FLContext) -> ModelLearnable:
-        """Convert Shareable to Learnable.
+        """Convert Shareable to ModelLearnable.
 
         Supporting TYPE == TYPE_WEIGHT_DIFF or TYPE_WEIGHTS
 
@@ -43,8 +43,16 @@ def shareable_to_learnable(self, shareable: Shareable, fl_ctx: FLContext) -> Mod
             shareable (Shareable): Shareable that contains a DXO object
             fl_ctx (FLContext): FL context
 
-        Returns: a ModelLearnable object
+        Returns:
+            A ModelLearnable object
+
+        Raises:
+            TypeError: if shareable is not of type shareable
+            ValueError: if data_kind is not `DataKind.WEIGHTS` and is not `DataKind.WEIGHT_DIFF`
         """
+        if not isinstance(shareable, Shareable):
+            raise TypeError("shareable must be Shareable, but got {}.".format(type(shareable)))
+
         base_model = fl_ctx.get_prop(AppConstants.GLOBAL_MODEL)
         if not base_model:
             self.system_panic(reason="No global base model!", fl_ctx=fl_ctx)
@@ -64,6 +72,10 @@ def shareable_to_learnable(self, shareable: Shareable, fl_ctx: FLContext) -> Mod
                 self.log_info(fl_ctx, "No model weights found. Model will not be updated.")
             else:
                 base_model[ModelLearnableKey.WEIGHTS] = weights
+        else:
+            raise ValueError(
+                "data_kind should be either DataKind.WEIGHTS or DataKind.WEIGHT_DIFF, but got {}".format(dxo.data_kind)
+            )
 
         base_model[ModelLearnableKey.META] = dxo.get_meta_props()
         return base_model


Test patch: