Repository: apache/airflow

Issue:
pdb no longer works with airflow test command since 2.3.3
Converted back to issue as I've reproduced it and traced the issue back to https://github.com/apache/airflow/pull/24362

### Discussed in https://github.com/apache/airflow/discussions/26352

<div type='discussions-op-text'>

<sup>Originally posted by **GuruComposer** September 12, 2022</sup>
### Apache Airflow version

2.3.4

### What happened

I used to be able to use ipdb to debug DAGs by running `airflow tasks test <dag_name> <dag_id>`, and hitting an ipdb breakpoint (ipdb.set_trace()).

This no longer works. I get a strange type error:

```  File "/usr/local/lib/python3.9/bdb.py", line 88, in trace_dispatch
    return self.dispatch_line(frame)
  File "/usr/local/lib/python3.9/bdb.py", line 112, in dispatch_line
    self.user_line(frame)
  File "/usr/local/lib/python3.9/pdb.py", line 262, in user_line
    self.interaction(frame, None)
  File "/home/astro/.local/lib/python3.9/site-packages/IPython/core/debugger.py", line 336, in interaction
    OldPdb.interaction(self, frame, traceback)
  File "/usr/local/lib/python3.9/pdb.py", line 357, in interaction
    self._cmdloop()
  File "/usr/local/lib/python3.9/pdb.py", line 322, in _cmdloop
    self.cmdloop()
  File "/usr/local/lib/python3.9/cmd.py", line 126, in cmdloop
    line = input(self.prompt)
TypeError: an integer is required (got type NoneType)```



### What you think should happen instead

I should get the ipdb shell.

### How to reproduce

1. Add ipdb breakpoint anywhere in airflow task.
import ipdb; ipdb.set_trace()

2. Run that task:
Run `airflow tasks test <dag_name> <dag_id>`, and 

### Operating System

Debian GNU/Linux

### Versions of Apache Airflow Providers

2.3.4


Discussion:
Minimal repro:


```python
from contextlib import redirect_stdout
from airflow.utils.log.secrets_masker import RedactedIO


with redirect_stdout(RedactedIO()):
    breakpoint()
    ...

```

```
airflow ❯ python testredir.py   
[0] > /home/ash/code/astro/upgrade-test-project/testredir.py(7)<module>()
-> ...
Traceback (most recent call last):
  File "/home/ash/code/astro/upgrade-test-project/testredir.py", line 7, in <module>
    ...
  File "/home/ash/code/astro/upgrade-test-project/testredir.py", line 7, in <module>
    ...
  File "/usr/lib/python3.10/bdb.py", line 90, in trace_dispatch
    return self.dispatch_line(frame)
  File "/usr/lib/python3.10/bdb.py", line 114, in dispatch_line
    self.user_line(frame)
  File "/usr/lib/python3.10/pdb.py", line 262, in user_line
    self.interaction(frame, None)
  File "/home/ash/.virtualenvs/airflow/lib/python3.10/site-packages/pdb.py", line 231, in interaction
    self._cmdloop()
  File "/usr/lib/python3.10/pdb.py", line 322, in _cmdloop
    self.cmdloop()
  File "/usr/lib/python3.10/cmd.py", line 126, in cmdloop
    line = input(self.prompt)
TypeError: 'NoneType' object cannot be interpreted as an integer
```

Resolving patch:
diff --git a/airflow/utils/log/secrets_masker.py b/airflow/utils/log/secrets_masker.py
--- a/airflow/utils/log/secrets_masker.py
+++ b/airflow/utils/log/secrets_masker.py
@@ -271,6 +271,7 @@ class RedactedIO(TextIO):
 
     def __init__(self):
         self.target = sys.stdout
+        self.fileno = sys.stdout.fileno
 
     def write(self, s: str) -> int:
         s = redact(s)


Test patch:
diff --git a/tests/utils/log/test_secrets_masker.py b/tests/utils/log/test_secrets_masker.py
--- a/tests/utils/log/test_secrets_masker.py
+++ b/tests/utils/log/test_secrets_masker.py
@@ -18,9 +18,11 @@
 
 import contextlib
 import inspect
+import io
 import logging
 import logging.config
 import os
+import sys
 import textwrap
 
 import pytest
@@ -363,3 +365,13 @@ def test_write(self, capsys):
         RedactedIO().write(p)
         stdout = capsys.readouterr().out
         assert stdout == "***"
+
+    def test_input_builtin(self, monkeypatch):
+        """
+        Test that when redirect is inplace the `input()` builtin works.
+
+        This is used by debuggers!
+        """
+        monkeypatch.setattr(sys, 'stdin', io.StringIO("a\n"))
+        with contextlib.redirect_stdout(RedactedIO()):
+            assert input() == "a"