From fd96855c8bc4fdd4fac3a4cbf045c6ee69306dc4 Mon Sep 17 00:00:00 2001 From: Parman Mohammadalizadeh Date: Thu, 23 Jul 2026 22:34:39 +0200 Subject: [PATCH 1/2] Fix WeaviateIngestOperator input_data validation before rendering input_data is a template field, rendered after __init__ runs. Validating it in the constructor checks the un-rendered value. Move the required-value check into execute() so it runs on the rendered value. Constructing with input_data=None now raises at run time rather than at build time. --- .../providers/weaviate/operators/weaviate.py | 7 ++++--- .../unit/weaviate/operators/test_weaviate.py | 15 +++++++++++++++ .../prek/validate_operators_init_exemptions.txt | 1 - 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py b/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py index de080c7232036..1e8d875bccd0c 100644 --- a/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py +++ b/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py @@ -75,15 +75,16 @@ def __init__( self.input_data = input_data self.hook_params = hook_params or {} - if self.input_data is None: - raise TypeError("input_data is required") - @cached_property def hook(self) -> WeaviateHook: """Return an instance of the WeaviateHook.""" return WeaviateHook(conn_id=self.conn_id, **self.hook_params) def execute(self, context: Context) -> None: + # input_data is a template field; validate it after rendering rather than in __init__, + # where the check would run against the un-rendered value. + if self.input_data is None: + raise TypeError("input_data is required") self.log.debug("Input data: %s", self.input_data) self.hook.batch_data( collection_name=self.collection_name, diff --git a/providers/weaviate/tests/unit/weaviate/operators/test_weaviate.py b/providers/weaviate/tests/unit/weaviate/operators/test_weaviate.py index 0f09fb35d528b..dbb94265f81ae 100644 --- a/providers/weaviate/tests/unit/weaviate/operators/test_weaviate.py +++ b/providers/weaviate/tests/unit/weaviate/operators/test_weaviate.py @@ -81,6 +81,21 @@ def test_execute_passes_tenant_to_hook(self): tenant="tenant-a", ) + def test_missing_input_data_raises_at_execute_not_init(self): + """ + input_data is a template field, so the required-value check runs in execute() + (after rendering), not in __init__. Constructing with input_data=None must not raise. + """ + operator = WeaviateIngestOperator( + task_id="weaviate_task", + conn_id="weaviate_conn", + collection_name="my_collection", + input_data=None, + ) + + with pytest.raises(TypeError, match="input_data is required"): + operator.execute(context=None) + @pytest.mark.db_test def test_templates(self, create_task_instance_of_operator): dag_id = "TestWeaviateIngestOperator" diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt b/scripts/ci/prek/validate_operators_init_exemptions.txt index 01fd5bc56dbb7..1f44441ca2521 100644 --- a/scripts/ci/prek/validate_operators_init_exemptions.txt +++ b/scripts/ci/prek/validate_operators_init_exemptions.txt @@ -88,4 +88,3 @@ providers/standard/src/airflow/providers/standard/operators/hitl.py::HITLOperato providers/standard/src/airflow/providers/standard/operators/trigger_dagrun.py::TriggerDagRunOperator providers/standard/src/airflow/providers/standard/sensors/date_time.py::DateTimeSensor providers/teradata/src/airflow/providers/teradata/transfers/teradata_to_teradata.py::TeradataToTeradataOperator -providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py::WeaviateIngestOperator From 11bf9ecf70f026fc9ce5184215b9467e4c60484b Mon Sep 17 00:00:00 2001 From: Parman Mohammadalizadeh Date: Fri, 24 Jul 2026 16:31:17 +0200 Subject: [PATCH 2/2] Make WeaviateIngestOperator input_data a required argument The default was left over from the removed input_json alternative. Dropping it restores parse-time validation of a missing input_data through Python itself and lets mypy catch input_data=None, while the execute() check still guards values that render to None after templating. --- .../src/airflow/providers/weaviate/operators/weaviate.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py b/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py index 1e8d875bccd0c..a52317393f078 100644 --- a/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py +++ b/providers/weaviate/src/airflow/providers/weaviate/operators/weaviate.py @@ -59,7 +59,7 @@ def __init__( self, conn_id: str, collection_name: str, - input_data: list[dict[str, Any]] | pd.DataFrame | None = None, + input_data: list[dict[str, Any]] | pd.DataFrame, vector_col: str = "Vector", uuid_column: str = "id", tenant: str | None = None, @@ -81,8 +81,6 @@ def hook(self) -> WeaviateHook: return WeaviateHook(conn_id=self.conn_id, **self.hook_params) def execute(self, context: Context) -> None: - # input_data is a template field; validate it after rendering rather than in __init__, - # where the check would run against the un-rendered value. if self.input_data is None: raise TypeError("input_data is required") self.log.debug("Input data: %s", self.input_data)