From dfd2814e84d0c04845ba0649a9a7a7b476ee456a Mon Sep 17 00:00:00 2001 From: bramhanandlingala Date: Sat, 25 Jul 2026 17:22:25 +0530 Subject: [PATCH] Fix template-field validation timing in PapermillOperator --- .../providers/papermill/operators/papermill.py | 8 ++++---- .../unit/papermill/operators/test_papermill.py | 13 ++++++++++--- .../ci/prek/validate_operators_init_exemptions.txt | 1 - 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/providers/papermill/src/airflow/providers/papermill/operators/papermill.py b/providers/papermill/src/airflow/providers/papermill/operators/papermill.py index f41240ab26387..f0ce1607afd9b 100644 --- a/providers/papermill/src/airflow/providers/papermill/operators/papermill.py +++ b/providers/papermill/src/airflow/providers/papermill/operators/papermill.py @@ -89,12 +89,8 @@ def __init__( super().__init__(**kwargs) self.parameters = parameters - if not input_nb: - raise ValueError("Input notebook is not specified") self.input_nb = input_nb - if not output_nb: - raise ValueError("Output notebook is not specified") self.output_nb = output_nb self.kernel_name = kernel_name @@ -105,6 +101,10 @@ def __init__( self.nbconvert_args = nbconvert_args def execute(self, context: Context): + if not self.input_nb: + raise ValueError("Input notebook is not specified") + if not self.output_nb: + raise ValueError("Output notebook is not specified") if not isinstance(self.input_nb, NoteBook): self.input_nb = NoteBook(url=self.input_nb, parameters=self.parameters) if not isinstance(self.output_nb, NoteBook): diff --git a/providers/papermill/tests/unit/papermill/operators/test_papermill.py b/providers/papermill/tests/unit/papermill/operators/test_papermill.py index f911fb8365843..b18c9e840238e 100644 --- a/providers/papermill/tests/unit/papermill/operators/test_papermill.py +++ b/providers/papermill/tests/unit/papermill/operators/test_papermill.py @@ -42,13 +42,20 @@ def test_templated_fields(self): class TestPapermillOperator: """Test PapermillOperator.""" + def test_init_does_not_validate_notebooks(self): + """__init__ must not validate template fields; input_nb/output_nb are rendered after construction.""" + op = PapermillOperator(task_id="missing_input_nb", output_nb="foo-bar") + assert op.input_nb is None + op = PapermillOperator(task_id="missing_output_nb", input_nb="foo-bar") + assert op.output_nb is None + def test_mandatory_attributes(self): - """Test missing Input or Output notebooks.""" + """Test missing Input or Output notebooks are validated at execute() time, after templating.""" with pytest.raises(ValueError, match="Input notebook is not specified"): - PapermillOperator(task_id="missing_input_nb", output_nb="foo-bar") + PapermillOperator(task_id="missing_input_nb", output_nb="foo-bar").execute(context={}) with pytest.raises(ValueError, match="Output notebook is not specified"): - PapermillOperator(task_id="missing_input_nb", input_nb="foo-bar") + PapermillOperator(task_id="missing_output_nb", input_nb="foo-bar").execute(context={}) @pytest.mark.parametrize( ("output_nb_url", "output_as_object"), diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt b/scripts/ci/prek/validate_operators_init_exemptions.txt index 29ee3dd263922..16afe1fe88e8c 100644 --- a/scripts/ci/prek/validate_operators_init_exemptions.txt +++ b/scripts/ci/prek/validate_operators_init_exemptions.txt @@ -55,7 +55,6 @@ providers/microsoft/azure/src/airflow/providers/microsoft/azure/transfers/gcs_to providers/microsoft/azure/src/airflow/providers/microsoft/azure/transfers/oracle_to_azure_data_lake.py::OracleToAzureDataLakeOperator providers/microsoft/psrp/src/airflow/providers/microsoft/psrp/operators/psrp.py::PsrpOperator providers/oracle/src/airflow/providers/oracle/transfers/oracle_to_oracle.py::OracleToOracleOperator -providers/papermill/src/airflow/providers/papermill/operators/papermill.py::PapermillOperator providers/standard/src/airflow/providers/standard/operators/bash.py::BashOperator providers/standard/src/airflow/providers/standard/operators/trigger_dagrun.py::TriggerDagRunOperator providers/standard/src/airflow/providers/standard/sensors/date_time.py::DateTimeSensor