shahar1 commented on code in PR #70740:
URL: https://github.com/apache/airflow/pull/70740#discussion_r3695204837


##########
providers/google/src/airflow/providers/google/cloud/transfers/azure_fileshare_to_gcs.py:
##########
@@ -127,6 +128,14 @@ def __init__(
         else:
             self.return_gcs_uris = return_gcs_uris
 
+    def render_template_fields(self, context: Context, jinja_env: 
jinja2.Environment | None = None) -> None:

Review Comment:
   This override won't run for mapped tasks. 
`MappedOperator.render_template_fields` calls 
`unmapped_task._do_render_template_fields(...)` directly rather than the 
operator's own `render_template_fields` — see the comment at 
[`mappedoperator.py:844-847`](https://github.com/apache/airflow/blob/main/task-sdk/src/airflow/sdk/definitions/mappedoperator.py#L844).
 I confirmed it with a scratch DAG: a subclass override fires for a plain task 
and not for a `.partial().expand()` one.
   
   So `AzureFileShareToGCSOperator.partial(directory_name="dir").expand(...)` 
regresses: `__init__` no longer aliases, the override never fires, and 
`execute` hits `RuntimeError: The directory_name must be set!.`. That path 
works on `main` today — `unmap()` goes through `__init__`, which aliased the 
raw template string into `directory_path`, and `_do_render_template_fields` 
then rendered it correctly. (Which also means the un-rendered-alias bug this PR 
fixes only ever affected non-mapped tasks.)
   
   Decide in `__init__` and apply in `execute` — that keeps your pre-render 
decision, which is the right call, and `execute` runs on both the mapped and 
unmapped paths:
   
   ```python
   # in __init__, replacing the render_template_fields override
   self._use_directory_name = directory_path is None and directory_name is not 
None
   
   # at the top of execute()
   if self._use_directory_name:
       self.directory_path = self.directory_name
   ```
   
   `_use_directory_name` isn't a template field, so `validate_operators_init` 
stays satisfied and the exemption removal still holds. If you'd rather keep it 
at render time, overriding `_do_render_template_fields` covers both paths too, 
but it's a private API — I'd lean toward the flag.
   
   Either way, please add a mapped-task test (`.partial(directory_name=...)` → 
assert `directory_path` after execute), since that's the case that regressed.



##########
providers/google/tests/unit/google/cloud/transfers/test_azure_fileshare_to_gcs.py:
##########
@@ -56,6 +61,96 @@ def test_init(self):
         assert operator.dest_gcs == GCS_PATH_PREFIX
         assert operator.google_impersonation_chain == IMPERSONATION_CHAIN
 
+    
@mock.patch("airflow.providers.google.cloud.transfers.azure_fileshare_to_gcs.AzureFileShareHook")
+    
@mock.patch("airflow.providers.google.cloud.transfers.azure_fileshare_to_gcs.GCSHook")
+    def test_directory_name_alias_uses_rendered_value(self, gcs_mock_hook, 
azure_fileshare_mock_hook):
+        """A templated directory_name is aliased to directory_path using its 
rendered value, not the Jinja expression."""
+        dag = DAG("test_azure_fileshare_alias", schedule=None, 
start_date=DEFAULT_DATE)
+        with pytest.warns(AirflowProviderDeprecationWarning, match="Use 
'directory_path' instead"):
+            operator = AzureFileShareToGCSOperator(
+                task_id=TASK_ID,
+                share_name=AZURE_FILESHARE_SHARE,
+                directory_name="{{ params.legacy_dir }}",
+                params={"legacy_dir": "rendered/dir"},
+                azure_fileshare_conn_id=AZURE_FILESHARE_CONN_ID,
+                gcp_conn_id=GCS_CONN_ID,
+                dest_gcs=GCS_PATH_PREFIX,
+                return_gcs_uris=True,
+                dag=dag,
+            )
+        assert operator.directory_path is None
+
+        operator.render_template_fields({"params": {"legacy_dir": 
"rendered/dir"}})
+        assert operator.directory_path == "rendered/dir"
+
+        azure_fileshare_mock_hook.return_value.list_files.return_value = 
MOCK_FILES
+        operator.execute(None)
+        azure_fileshare_mock_hook.assert_any_call(
+            share_name=AZURE_FILESHARE_SHARE,
+            azure_fileshare_conn_id=AZURE_FILESHARE_CONN_ID,
+            directory_path="rendered/dir",
+        )
+
+    
@mock.patch("airflow.providers.google.cloud.transfers.azure_fileshare_to_gcs.AzureFileShareHook")
+    
@mock.patch("airflow.providers.google.cloud.transfers.azure_fileshare_to_gcs.GCSHook")
+    def test_native_directory_path_rendering_to_none_is_not_aliased(
+        self, gcs_mock_hook, azure_fileshare_mock_hook
+    ):
+        """
+        Behaviour-preservation guard for the __init__ -> 
render_template_fields move.
+
+        Deciding the alias from the pre-render values must be kept: with 
render_template_as_native_obj
+        an explicitly supplied directory_path can render to None, and 
re-inferring the alias afterwards
+        would wrongly fall back to the deprecated directory_name. This passes 
on the unrefactored code
+        (the provision decision was made in __init__) and fails if the 
decision is moved past rendering.
+        """
+        dag = DAG(
+            "test_azure_fileshare_native",
+            schedule=None,
+            start_date=DEFAULT_DATE,
+            render_template_as_native_obj=True,
+        )
+        operator = AzureFileShareToGCSOperator(
+            task_id=TASK_ID,
+            share_name=AZURE_FILESHARE_SHARE,
+            directory_path="{{ params.p }}",
+            directory_name="legacy",
+            params={"p": None},
+            azure_fileshare_conn_id=AZURE_FILESHARE_CONN_ID,
+            gcp_conn_id=GCS_CONN_ID,
+            dest_gcs=GCS_PATH_PREFIX,
+            return_gcs_uris=True,
+            dag=dag,
+        )
+
+        operator.render_template_fields({"params": {"p": None}})
+        assert operator.directory_path is None
+
+        # The deprecated alias must not silently substitute directory_name; a 
genuinely unset
+        # directory surfaces as the operator's own error instead of listing 
the wrong directory.
+        azure_fileshare_mock_hook.return_value.list_files.return_value = 
MOCK_FILES
+        with pytest.raises(RuntimeError, match="directory_name must be set"):
+            operator.execute(None)
+
+    @pytest.mark.parametrize(
+        "extra_kwargs",
+        [
+            pytest.param({"directory_path": AZURE_FILESHARE_DIRECTORY_PATH}, 
id="directory_path-supplied"),
+            pytest.param({}, id="neither-supplied"),
+        ],
+    )
+    def test_no_directory_name_deprecation_without_directory_name(self, 
extra_kwargs):

Review Comment:
   This test passes unchanged on `main` — the `__init__` condition is 
semantically identical before and after 
(`self.directory_path`/`self.directory_name` were assigned unmodified from the 
parameters just above), so it exercises pre-existing behaviour rather than 
anything this PR changes.
   
   [AGENTS.md Testing 
Standards](https://github.com/apache/airflow/blob/main/AGENTS.md#testing-standards):
 *"Target exactly 100% coverage of what the PR changes — no more, no less... 
every test must fail without the PR's change. Do not add tests for pre-existing 
logic that was already present before the PR."*
   
   Worth dropping, unless you're deliberately backfilling a gap — in which case 
a one-line note saying so would help.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to