potiuk commented on code in PR #63878:
URL: https://github.com/apache/airflow/pull/63878#discussion_r3215299268


##########
shared/logging/tests/logging/test_structlog.py:
##########
@@ -512,6 +514,17 @@ def spy(et, ev, tb):
         sys.excepthook = original
 
 
+def test_init_log_folder_permission_error_logs_warning_and_continues(caplog):
+    """Uses caplog to assert stdlib logging output from init_log_folder 
(exception path)."""
+    from airflow_shared.logging.structlog import init_log_folder
+
+    with mock.patch.object(Path, "mkdir", side_effect=PermissionError("not 
allowed")):
+        with caplog.at_level(logging.WARNING):
+            init_log_folder("/tmp/blocked", 0o775)
+
+    assert "Could not create log folder" in caplog.text

Review Comment:
   [`CLAUDE.md`](https://github.com/apache/airflow/blob/main/CLAUDE.md) is 
explicit on this: *"Do not use `caplog` in tests, prefer checking logic and not 
log output."*
   
   The load-bearing contract for callers of `init_log_folder` is *"doesn't 
raise on filesystem errors so CLI startup continues"*. The exact warning text 
is implementation detail — if someone reworded the message tomorrow, this test 
would fail without anything actually breaking.
   
   Suggested rewrite:
   
   ```python
   def test_init_log_folder_does_not_raise_on_permission_error():
       from airflow_shared.logging.structlog import init_log_folder
   
       with mock.patch.object(Path, "mkdir", side_effect=PermissionError("not 
allowed")):
           # Must not raise — CLI commands like `airflow db migrate` rely on 
this.
           init_log_folder("/tmp/blocked", 0o775)
   ```
   
   If the test stops using `caplog`, the comment-block update at lines 39–41 of 
this file is no longer needed either — please revert it back to the original 
two-line comment.
   
   ---
   Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting



##########
shared/logging/src/airflow_shared/logging/structlog.py:
##########
@@ -756,9 +756,15 @@ def init_log_folder(directory: str | os.PathLike[str], 
new_folder_permissions: i
     user.
     """
     directory = _PatchedPath(directory)
-    for parent in reversed(_PatchedPath(directory).parents):
-        parent.mkdir(mode=new_folder_permissions, exist_ok=True)
-    directory.mkdir(mode=new_folder_permissions, exist_ok=True)
+    try:
+        directory.mkdir(mode=new_folder_permissions, parents=True, 
exist_ok=True)
+    except PermissionError as e:

Review Comment:
   Catching only `PermissionError` is too narrow for "best-effort log dir 
creation". Other `OSError` subclasses can hit on the same code path:
   
   - `OSError(EROFS)` — read-only filesystem (e.g. recovery mode, broken bind 
mount)
   - `OSError(ENOSPC)` — disk full
   - `OSError(EIO)` — I/O error on a flaky NFS mount or hardware fault
   - `OSError(ESTALE)` — stale NFS file handle
   
   None of these are `PermissionError`, so they'd still crash CLI commands. The 
peer function `init_log_file` already catches the broader `OSError` (see line 
783) for exactly the same reason — worth being consistent:
   
   ```python
   except OSError as e:
   ```
   
   Side note: the PR description says the catch is `(PermissionError, OSError)` 
— the broader form may have been lost in a rebase.
   
   ---
   Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting



-- 
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