Repository: kubeflow/pipelines

Issue:
feat(backend): Support optional input parameters



Discussion:

Resolving patch:
diff --git a/samples/v2/component_with_optional_inputs.py b/samples/v2/component_with_optional_inputs.py
new file mode 100644
--- /dev/null
+++ b/samples/v2/component_with_optional_inputs.py
@@ -0,0 +1,52 @@
+# Copyright 2023 The Kubeflow Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+from typing import Optional, Dict, List
+
+from kfp import compiler
+from kfp import dsl
+from kfp.dsl import component
+
+
+@component
+def component_op(
+    input_str1: Optional[str] = 'string default value',
+    input_str2: Optional[str] = None,
+    input_str3: Optional[str] = None,
+    input_bool1: Optional[bool] = True,
+    input_bool2: Optional[bool] = None,
+    input_dict: Optional[Dict[str, int]] = {"a": 1},
+    input_list: Optional[List[str]] = ["123"],
+    input_int: Optional[int] = 100,
+):
+    print(f'input_str1: {input_str1}, type: {type(input_str1)}')
+    print(f'input_str2: {input_str2}, type: {type(input_str2)}')
+    print(f'input_str3: {input_str3}, type: {type(input_str3)}')
+    print(f'input_bool1: {input_bool1}, type: {type(input_bool1)}')
+    print(f'input_bool2: {input_bool2}, type: {type(input_bool2)}')
+    print(f'input_bool: {input_dict}, type: {type(input_dict)}')
+    print(f'input_bool: {input_list}, type: {type(input_list)}')
+    print(f'input_bool: {input_int}, type: {type(input_int)}')
+
+
+@dsl.pipeline(name='v2-component-optional-input')
+def pipeline():
+    component_op(
+        input_str1='Hello',
+        input_str2='World',
+    )
+
+
+if __name__ == '__main__':
+    compiler.Compiler().compile(
+        pipeline_func=pipeline, package_path=__file__.replace('.py', '.yaml'))


Test patch:
diff --git a/samples/test/config.yaml b/samples/test/config.yaml
--- a/samples/test/config.yaml
+++ b/samples/test/config.yaml
@@ -126,6 +126,8 @@
   path: samples.v2.pipeline_container_no_input_test
 - name: two_step_pipeline_containerized
   path: samples.v2.two_step_pipeline_containerized_test
+- name: component_with_optional_inputs
+  path: samples.v2.component_with_optional_inputs_test
 - name: pipeline_with_env
   path: samples.v2.pipeline_with_env_test
 # TODO(capri-xiyue): Re-enable after figuring out V2 Engine
diff --git a/samples/test/lightweight_python_functions_v2_pipeline_test.py b/samples/test/lightweight_python_functions_v2_pipeline_test.py
--- a/samples/test/lightweight_python_functions_v2_pipeline_test.py
+++ b/samples/test/lightweight_python_functions_v2_pipeline_test.py
@@ -40,7 +40,6 @@ def verify(run: kfp_server_api.ApiRun, mlmd_connection_config, **kwargs):
         {
             'inputs': {
                 'parameters': {
-                    'empty_message': '',
                     'message': 'message',
                 }
             },
@@ -97,8 +96,7 @@ def verify(run: kfp_server_api.ApiRun, mlmd_connection_config, **kwargs):
                         "B": 2.0,
                     },
                     'input_list': ["a", "b", "c"],
-                    'message': 'message',
-                    'num_steps': 100.0
+                    'message': 'message'
                 }
             },
             'name': 'train',
diff --git a/samples/v2/component_with_optional_inputs_test.py b/samples/v2/component_with_optional_inputs_test.py
new file mode 100644
--- /dev/null
+++ b/samples/v2/component_with_optional_inputs_test.py
@@ -0,0 +1,58 @@
+# Copyright 2023 The Kubeflow Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+"""Component with optinal and default input v2 engine pipeline."""
+
+from __future__ import annotations
+
+import unittest
+
+import kfp.deprecated as kfp
+from kfp.samples.test.utils import KfpTask
+from kfp.samples.test.utils import run_pipeline_func
+from kfp.samples.test.utils import TaskInputs
+from kfp.samples.test.utils import TaskOutputs
+from kfp.samples.test.utils import TestCase
+import kfp_server_api
+from ml_metadata.proto import Execution
+
+from .component_with_optional_inputs import pipeline
+
+
+def verify(t: unittest.TestCase, run: kfp_server_api.ApiRun,
+           tasks: dict[str, KfpTask], **kwargs):
+    t.assertEqual(run.status, 'Succeeded')
+    component_op_dict = tasks['component-op'].get_dict()
+
+    t.assertEqual(
+        {
+            'name': 'component-op',
+            'inputs': {
+                'parameters': {
+                    'input_str1': 'Hello',
+                    'input_str2': 'World',
+                },
+            },
+            'outputs': {},
+            'type': 'system.ContainerExecution',
+            'state': Execution.State.COMPLETE,
+        }, component_op_dict)
+
+if __name__ == '__main__':
+    run_pipeline_func([
+        TestCase(
+            pipeline_func=pipeline,
+            verify_func=verify,
+            mode=kfp.dsl.PipelineExecutionMode.V2_ENGINE,
+        ),
+    ])