From f7e5dcb9d3360f3d1d64e00e40ad543892e82854 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Fri, 9 Jun 2023 14:27:28 +0200 Subject: [PATCH 01/11] add writer component --- fondant/component.py | 58 +++++++++++++++++++++++++++++++++++++++-- tests/test_component.py | 43 +++++++++++++++++++++++------- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/fondant/component.py b/fondant/component.py index 7594bcfaf..adb11ebcb 100644 --- a/fondant/component.py +++ b/fondant/component.py @@ -112,7 +112,7 @@ def _load_or_create_manifest(self) -> Manifest: """Abstract method that returns the dataset manifest.""" @abstractmethod - def _process_dataset(self, manifest: Manifest) -> dd.DataFrame: + def _process_dataset(self, manifest: Manifest) -> t.Union[None, dd.DataFrame]: """Abstract method that processes the manifest and returns another dataframe. """ @@ -223,7 +223,7 @@ def transform(self, *args, **kwargs) -> dd.DataFrame: kwargs: Arguments provided to the component are passed as keyword arguments """ - def _process_dataset(self, manifest: Manifest) -> dd.DataFrame: + def _process_dataset(self, manifest: Manifest) -> t.Union[None, dd.DataFrame]: """ Creates a DataLoader using the provided manifest and loads the input dataframe using the `load_dataframe` instance, and applies data transformations to it using the `transform` @@ -237,3 +237,57 @@ def _process_dataset(self, manifest: Manifest) -> dd.DataFrame: df = self.transform(dataframe=df, **self.user_arguments) return df + + +class WriteComponent(Component): + """Base class for a Fondant write component.""" + + @classmethod + def _add_and_parse_args(cls, spec: ComponentSpec): + parser = argparse.ArgumentParser() + component_arguments = cls._get_component_arguments(spec) + + for arg in component_arguments.values(): + parser.add_argument( + f"--{arg.name}", + type=kubeflow2python_type[arg.type], # type: ignore + required=True, + help=arg.description, + ) + + return parser.parse_args() + + def _load_or_create_manifest(self) -> Manifest: + return Manifest.from_file(self.input_manifest_path) + + @abstractmethod + def write(self, *args, **kwargs): + """ + Abstract method to write a dataframe to a final custom location. + + Args: + args: The dataframe will be passed in as a positional argument + kwargs: Arguments provided to the component are passed as keyword arguments + """ + + def _process_dataset(self, manifest: Manifest) -> t.Union[None, dd.DataFrame]: + """ + Creates a DataLoader using the provided manifest and loads the input dataframe using the + `load_dataframe` instance, and applies data transformations to it using the `transform` + method implemented by the derived class. Returns a single dataframe. + + Returns: + A `dd.DataFrame` instance with updated data based on the applied data transformations. + """ + data_loader = DaskDataLoader(manifest=manifest, component_spec=self.spec) + df = data_loader.load_dataframe() + self.write(dataframe=df, **self.user_arguments) + + return None + + def _write_data(self, dataframe: dd.DataFrame, *, manifest: Manifest): + """Create a data writer given a manifest and writes out the index and subsets.""" + pass + + def upload_manifest(self, manifest: Manifest, save_path: str): + pass diff --git a/tests/test_component.py b/tests/test_component.py index 4c8f267c0..d35ff8277 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -10,7 +10,7 @@ import pytest import yaml -from fondant.component import LoadComponent, TransformComponent +from fondant.component import LoadComponent, TransformComponent, WriteComponent from fondant.data_io import DaskDataLoader components_path = Path(__file__).parent / "example_specs/components" @@ -75,8 +75,10 @@ def test_component(mock_args): } -def test_valid_transform_kwargs(monkeypatch): - """Test that arguments are passed correctly to `Component.transform` method.""" +def test_transform_component(monkeypatch): + """Test that arguments are passed correctly to `Component.transform` method and that valid + errors are returned when required arguments are missing. + """ class EarlyStopException(Exception): """Used to stop execution early instead of mocking all later functionality.""" @@ -120,12 +122,23 @@ def transform(self, dataframe, *, flag, value): # Instantiate and run component component = MyComponent.from_args() + with pytest.raises(EarlyStopException): component.run() + # Remove component specs from arguments + component_spec_index = sys.argv.index("--component_spec") + del sys.argv[component_spec_index : component_spec_index + 2] + + # Instantiate and run component + with pytest.raises(ValueError): + MyComponent.from_args() + -def test_invalid_transform_kwargs(monkeypatch): - """Test that arguments are passed correctly to `Component.transform` method.""" +def test_write_component(tmp_path_factory, monkeypatch): + """Test that arguments are passed correctly to `Component.write` method and that valid + errors are returned when required arguments are missing. + """ class EarlyStopException(Exception): """Used to stop execution early instead of mocking all later functionality.""" @@ -141,14 +154,16 @@ def mocked_load_dataframe(self): component_spec = arguments_dir / "component.yaml" input_manifest = arguments_dir / "input_manifest.json" - yaml_file_to_json_string(component_spec) + component_spec_string = yaml_file_to_json_string(component_spec) # Implemented Component class - class MyComponent(TransformComponent): - def transform(self, dataframe, *, flag, value): + class MyComponent(WriteComponent): + def write(self, dataframe, *, flag, value): assert flag == "success" assert value == 1 - raise EarlyStopException() + # Mock write function that sinks final data to a local directory + with tmp_path_factory.mktemp("temp") as fn: + dataframe.to_parquet(fn) # Mock CLI arguments sys.argv = [ @@ -163,8 +178,18 @@ def transform(self, dataframe, *, flag, value): "1", "--output_manifest_path", "", + "--component_spec", + f"{component_spec_string}", ] + # # Instantiate and run component + component = MyComponent.from_args() + component.run() + + # Remove component specs from arguments + component_spec_index = sys.argv.index("--component_spec") + del sys.argv[component_spec_index : component_spec_index + 2] + # Instantiate and run component with pytest.raises(ValueError): MyComponent.from_args() From 17c5c7e176649f687d88aa66c41088292577195d Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 14:40:24 +0200 Subject: [PATCH 02/11] modify component spec schema to accept default arguments --- fondant/schemas/component_spec.json | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/fondant/schemas/component_spec.json b/fondant/schemas/component_spec.json index d196c3db3..2183a3b3d 100644 --- a/fondant/schemas/component_spec.json +++ b/fondant/schemas/component_spec.json @@ -80,6 +80,49 @@ }, "description": { "type": "string" + }, + "default_value": { + "oneOf": [ + { + "type": "array" + }, + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number", + "format": "float" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array", + "items": { + "type": [ + "array", + "string", + "integer", + { + "type": "number", + "format": "float" + }, + "boolean", + "object" + ] + }, + "uniqueItems": true + }, + { + "type": "null" + } + ] } }, "required": [ From a4e44d2e3bf5f77854859dc330f4e4c89aae7d20 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 14:41:41 +0200 Subject: [PATCH 03/11] enable adding default arguments --- fondant/component.py | 95 +++++++++++++++++---------------------- fondant/component_spec.py | 6 +++ 2 files changed, 47 insertions(+), 54 deletions(-) diff --git a/fondant/component.py b/fondant/component.py index adb11ebcb..b938d606c 100644 --- a/fondant/component.py +++ b/fondant/component.py @@ -77,7 +77,6 @@ def from_spec(cls, component_spec: ComponentSpec) -> "Component": metadata = args_dict.pop("metadata") metadata = json.loads(metadata) if metadata else {} - return cls( component_spec, input_manifest_path=input_manifest_path, @@ -86,6 +85,38 @@ def from_spec(cls, component_spec: ComponentSpec) -> "Component": user_arguments=args_dict, ) + @classmethod + def _add_and_parse_args(cls, spec: ComponentSpec): + parser = argparse.ArgumentParser() + component_arguments = cls._get_component_arguments(spec) + + for arg in component_arguments.values(): + # Input manifest is not required for loading component + if arg.name in cls.optional_fondant_arguments(): + input_required = False + default = None + elif arg.default: + input_required = False + default = arg.default + else: + input_required = True + default = None + + parser.add_argument( + f"--{arg.name}", + type=kubeflow2python_type[arg.type], # type: ignore + required=input_required, + default=default, + help=arg.description, + ) + + return parser.parse_args() + + @staticmethod + @abstractmethod + def optional_fondant_arguments() -> t.List[str]: + pass + @staticmethod def _get_component_arguments(spec: ComponentSpec) -> t.Dict[str, Argument]: """ @@ -102,11 +133,6 @@ def _get_component_arguments(spec: ComponentSpec) -> t.Dict[str, Argument]: component_arguments.update(kubeflow_component_spec.output_arguments) return component_arguments - @classmethod - @abstractmethod - def _add_and_parse_args(cls, spec: ComponentSpec) -> argparse.Namespace: - """Abstract method to add and parse the component arguments.""" - @abstractmethod def _load_or_create_manifest(self) -> Manifest: """Abstract method that returns the dataset manifest.""" @@ -142,26 +168,9 @@ def upload_manifest(self, manifest: Manifest, save_path: str): class LoadComponent(Component): """Base class for a Fondant load component.""" - @classmethod - def _add_and_parse_args(cls, spec: ComponentSpec): - parser = argparse.ArgumentParser() - component_arguments = cls._get_component_arguments(spec) - - for arg in component_arguments.values(): - # Input manifest is not required for loading component - if arg.name == "input_manifest_path": - input_required = False - else: - input_required = True - - parser.add_argument( - f"--{arg.name}", - type=kubeflow2python_type[arg.type], # type: ignore - required=input_required, - help=arg.description, - ) - - return parser.parse_args() + @staticmethod + def optional_fondant_arguments() -> t.List[str]: + return ["input_manifest_path"] def _load_or_create_manifest(self) -> Manifest: # create initial manifest @@ -195,20 +204,9 @@ def _process_dataset(self, manifest: Manifest) -> dd.DataFrame: class TransformComponent(Component): """Base class for a Fondant transform component.""" - @classmethod - def _add_and_parse_args(cls, spec: ComponentSpec): - parser = argparse.ArgumentParser() - component_arguments = cls._get_component_arguments(spec) - - for arg in component_arguments.values(): - parser.add_argument( - f"--{arg.name}", - type=kubeflow2python_type[arg.type], # type: ignore - required=True, - help=arg.description, - ) - - return parser.parse_args() + @staticmethod + def optional_fondant_arguments() -> t.List[str]: + return [] def _load_or_create_manifest(self) -> Manifest: return Manifest.from_file(self.input_manifest_path) @@ -242,20 +240,9 @@ def _process_dataset(self, manifest: Manifest) -> t.Union[None, dd.DataFrame]: class WriteComponent(Component): """Base class for a Fondant write component.""" - @classmethod - def _add_and_parse_args(cls, spec: ComponentSpec): - parser = argparse.ArgumentParser() - component_arguments = cls._get_component_arguments(spec) - - for arg in component_arguments.values(): - parser.add_argument( - f"--{arg.name}", - type=kubeflow2python_type[arg.type], # type: ignore - required=True, - help=arg.description, - ) - - return parser.parse_args() + @staticmethod + def optional_fondant_arguments() -> t.List[str]: + return [] def _load_or_create_manifest(self) -> Manifest: return Manifest.from_file(self.input_manifest_path) diff --git a/fondant/component_spec.py b/fondant/component_spec.py index 40f88e4dc..b9dbf6261 100644 --- a/fondant/component_spec.py +++ b/fondant/component_spec.py @@ -46,11 +46,14 @@ class Argument: name: name of the argument description: argument description type: the python argument type (str, int, ...) + default: default value of the argument (defaults to None) + """ name: str description: str type: str + default: t.Optional[str] = None class ComponentSubset: @@ -180,6 +183,7 @@ def args(self) -> t.Dict[str, Argument]: name=name, description=arg_info["description"], type=arg_info["type"], + default=arg_info["default"] if "default" in arg_info else None, ) for name, arg_info in self._specification.get("args", {}).items() } @@ -236,6 +240,7 @@ def from_fondant_component_spec( "name": arg.name, "description": arg.description, "type": python2kubeflow_type[arg.type], + **({"default": arg.default} if arg.default is not None else {}), } for arg in fondant_component.args.values() ), @@ -305,6 +310,7 @@ def input_arguments(self) -> t.Mapping[str, Argument]: name=info["name"], description=info["description"], type=info["type"], + default=info["default"] if "default" in info else None, ) for info in self._specification["inputs"] } From 6d2d1ab9425da2321b8af1705a287d82bb532669 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 14:42:06 +0200 Subject: [PATCH 04/11] test adding default arguments --- .../arguments/component_default_args.yaml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/example_specs/components/arguments/component_default_args.yaml diff --git a/tests/example_specs/components/arguments/component_default_args.yaml b/tests/example_specs/components/arguments/component_default_args.yaml new file mode 100644 index 000000000..124c1d362 --- /dev/null +++ b/tests/example_specs/components/arguments/component_default_args.yaml @@ -0,0 +1,29 @@ +name: Example component +description: This is an example component +image: example_component:latest + +args: + string_default_arg: + description: default string argument + type: str + default: foo + integer_default_arg: + description: default integer argument + type: int + default: 1 + float_default_arg: + description: default float argument + type: float + default: 3.14 + bool_default_arg: + description: default bool argument + type: bool + default: 'False' + list_default_arg: + description: default list argument + type: list + default: '["foo", "bar"]' + dict_default_arg: + description: default dict argument + type: dict + default: '{"foo":1, "bar":2}' From af597f82255cae270f495299b0ca122907941586 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 14:44:53 +0200 Subject: [PATCH 05/11] fixmypy issue --- tests/test_component.py | 61 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/tests/test_component.py b/tests/test_component.py index d35ff8277..42c2214f8 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -140,9 +140,6 @@ def test_write_component(tmp_path_factory, monkeypatch): errors are returned when required arguments are missing. """ - class EarlyStopException(Exception): - """Used to stop execution early instead of mocking all later functionality.""" - # Mock `Dataset.load_dataframe` so no actual data is loaded def mocked_load_dataframe(self): return dd.from_dict({"a": [1, 2, 3]}, npartitions=1) @@ -193,3 +190,61 @@ def write(self, dataframe, *, flag, value): # Instantiate and run component with pytest.raises(ValueError): MyComponent.from_args() + + +def test_default_args_component(tmp_path_factory, monkeypatch): + """Test that arguments are passed correctly to `Component.write` method and that valid + errors are returned when required arguments are missing. + """ + + # Mock `Dataset.load_dataframe` so no actual data is loaded + def mocked_load_dataframe(self): + return dd.from_dict({"a": [1, 2, 3]}, npartitions=1) + + monkeypatch.setattr(DaskDataLoader, "load_dataframe", mocked_load_dataframe) + + # Define paths to specs to instantiate component + arguments_dir = components_path / "arguments" + component_spec = arguments_dir / "component_default_args.yaml" + input_manifest = arguments_dir / "input_manifest.json" + + component_spec_string = yaml_file_to_json_string(component_spec) + + # Implemented Component class + class MyComponent(WriteComponent): + def write( + self, + dataframe, + *, + string_default_arg, + integer_default_arg, + float_default_arg, + bool_default_arg, + list_default_arg, + dict_default_arg, + ): + float_const = 3.14 + # Mock write function that sinks final data to a local directory + assert string_default_arg == "foo" + assert integer_default_arg == 1 + assert float_default_arg == float_const + assert bool_default_arg is False + assert list_default_arg == ["foo", "bar"] + assert dict_default_arg == {"foo": 1, "bar": 2} + + # Mock CLI arguments + sys.argv = [ + "", + "--input_manifest_path", + str(input_manifest), + "--metadata", + "", + "--output_manifest_path", + "", + "--component_spec", + f"{component_spec_string}", + ] + + # # Instantiate and run component + component = MyComponent.from_args() + component.run() From c90b53bb31726f0acb1809a9fda806ae335f28b6 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 14:48:11 +0200 Subject: [PATCH 06/11] correct docs --- tests/test_component.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_component.py b/tests/test_component.py index 42c2214f8..6a98052be 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -193,8 +193,8 @@ def write(self, dataframe, *, flag, value): def test_default_args_component(tmp_path_factory, monkeypatch): - """Test that arguments are passed correctly to `Component.write` method and that valid - errors are returned when required arguments are missing. + """Test that default arguments defined in the fondant spec are passed correctly and have the + proper data type. """ # Mock `Dataset.load_dataframe` so no actual data is loaded From a8e55d81f90625854dfc4526a99ee08e8453afb6 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 14:59:52 +0200 Subject: [PATCH 07/11] update component spec --- fondant/schemas/component_spec.json | 37 +------------------ .../arguments/component_default_args.yaml | 4 ++ tests/test_component.py | 4 ++ 3 files changed, 10 insertions(+), 35 deletions(-) diff --git a/fondant/schemas/component_spec.json b/fondant/schemas/component_spec.json index 2183a3b3d..aee436c00 100644 --- a/fondant/schemas/component_spec.json +++ b/fondant/schemas/component_spec.json @@ -81,46 +81,13 @@ "description": { "type": "string" }, - "default_value": { + "default": { "oneOf": [ - { - "type": "array" - }, { "type": "string" }, { - "type": "integer" - }, - { - "type": "number", - "format": "float" - }, - { - "type": "boolean" - }, - { - "type": "object" - }, - { - "type": "array", - "items": { - "type": [ - "array", - "string", - "integer", - { - "type": "number", - "format": "float" - }, - "boolean", - "object" - ] - }, - "uniqueItems": true - }, - { - "type": "null" + "type": "number" } ] } diff --git a/tests/example_specs/components/arguments/component_default_args.yaml b/tests/example_specs/components/arguments/component_default_args.yaml index 124c1d362..7a1aabb01 100644 --- a/tests/example_specs/components/arguments/component_default_args.yaml +++ b/tests/example_specs/components/arguments/component_default_args.yaml @@ -27,3 +27,7 @@ args: description: default dict argument type: dict default: '{"foo":1, "bar":2}' + override_default_string_arg: + description: default argument that can be overriden + type: str + default: foo diff --git a/tests/test_component.py b/tests/test_component.py index 6a98052be..537899032 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -222,6 +222,7 @@ def write( bool_default_arg, list_default_arg, dict_default_arg, + override_default_string_arg, ): float_const = 3.14 # Mock write function that sinks final data to a local directory @@ -231,6 +232,7 @@ def write( assert bool_default_arg is False assert list_default_arg == ["foo", "bar"] assert dict_default_arg == {"foo": 1, "bar": 2} + assert override_default_string_arg == "bar" # Mock CLI arguments sys.argv = [ @@ -243,6 +245,8 @@ def write( "", "--component_spec", f"{component_spec_string}", + "--override_default_string_arg", + "bar", ] # # Instantiate and run component From 00dfa5531a89f1423286ee7095fe685454b03f0f Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Mon, 12 Jun 2023 15:17:48 +0200 Subject: [PATCH 08/11] update docs --- docs/component_spec.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/component_spec.md b/docs/component_spec.md index 6d11e6184..fda0f1922 100644 --- a/docs/component_spec.md +++ b/docs/component_spec.md @@ -121,41 +121,52 @@ Please check the [examples](#examples) below to build a better understanding. ### Args The `args` section describes which arguments the component takes. Each argument is defined by a -`description` and a `type`, which should be one of the builtin Python types. +`description` and a `type`, which should be one of the builtin Python types. Additionally, you can +pass an optional argument in the optional `default` field of the arguments. This can be useful in case +your function has many arguments that could be set to default and that don't need to be explicitly defined when +initializing your component. +_Note:_ default iterable arguments such as `dict` and `list` have to be passed as a string +(e.g. `'{"foo":1, "bar":2}`, `'["foo","bar]'`) ```yaml args: custom_argument: description: A custom argument type: str + default_argument: + description: A default argument + type: str + default: bar ``` -These arguments are passed in when the component is instantiated: - +These arguments are passed in when the component is instantiated. Notice that we are not passing the +default argument specified above. You could override the default value in the component spec by passing +it as an argument to the component. ```python from fondant.pipeline import ComponentOp custom_op = ComponentOp( component_spec_path="components/custom_component/fondant_component.yaml", arguments={ - "custom_argument": "foobar" + "custom_argument": "foo" }, ) ``` -And passed as a keyword argument to the `transform()` method of the component. +Afterwards, we pass all keyword arguments to the `transform()` method of the component. ```python from fondant.component import TransformComponent class ExampleComponent(TransformComponent): - def transform(self, dataframe, *, custom_argument): + def transform(self, dataframe, *, custom_argument, default_argument): """Implement your custom logic in this single method Args: dataframe: A Dask dataframe containing the data custom_argument: An argument passed to the component + default_argument: A default argument passed to the components """ ``` From fb822174bb39141917e91ea23429d4bd4eb7ba11 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Tue, 13 Jun 2023 13:15:41 +0200 Subject: [PATCH 09/11] Update docs/component_spec.md Co-authored-by: Robbe Sneyders --- docs/component_spec.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/component_spec.md b/docs/component_spec.md index fda0f1922..67c95a851 100644 --- a/docs/component_spec.md +++ b/docs/component_spec.md @@ -122,9 +122,7 @@ Please check the [examples](#examples) below to build a better understanding. The `args` section describes which arguments the component takes. Each argument is defined by a `description` and a `type`, which should be one of the builtin Python types. Additionally, you can -pass an optional argument in the optional `default` field of the arguments. This can be useful in case -your function has many arguments that could be set to default and that don't need to be explicitly defined when -initializing your component. +set an optional `default` value for each argument. _Note:_ default iterable arguments such as `dict` and `list` have to be passed as a string (e.g. `'{"foo":1, "bar":2}`, `'["foo","bar]'`) From 07a4dfbdad991a2b56276ddc1def220343504abd Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Tue, 13 Jun 2023 13:15:54 +0200 Subject: [PATCH 10/11] Update docs/component_spec.md Co-authored-by: Robbe Sneyders --- docs/component_spec.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/component_spec.md b/docs/component_spec.md index 67c95a851..00022103c 100644 --- a/docs/component_spec.md +++ b/docs/component_spec.md @@ -137,9 +137,8 @@ args: default: bar ``` -These arguments are passed in when the component is instantiated. Notice that we are not passing the -default argument specified above. You could override the default value in the component spec by passing -it as an argument to the component. +These arguments are passed in when the component is instantiated. +If an argument is not explicitly provided, the default value will be used instead if available.``` ```python from fondant.pipeline import ComponentOp From 51d7ea76e29ffbf02ae9affcb214ce2b876c0cb3 Mon Sep 17 00:00:00 2001 From: Philippe Moussalli Date: Tue, 13 Jun 2023 13:26:59 +0200 Subject: [PATCH 11/11] add output manifest path to optional writer arguments --- fondant/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fondant/component.py b/fondant/component.py index b938d606c..f61c09585 100644 --- a/fondant/component.py +++ b/fondant/component.py @@ -242,7 +242,7 @@ class WriteComponent(Component): @staticmethod def optional_fondant_arguments() -> t.List[str]: - return [] + return ["output_manifest_path"] def _load_or_create_manifest(self) -> Manifest: return Manifest.from_file(self.input_manifest_path)