Repository: apache/airflow

Issue:
Superfluous TypeError when passing not-iterables to `expand()`
### Apache Airflow version

main (development)

### What happened

Here's a problematic dag.  `False` is invalid here.
```python3
from airflow.models import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta

with DAG(
    dag_id="singleton_expanded",
    schedule_interval=timedelta(days=365),
    start_date=datetime(2001, 1, 1),
) as dag:

    # has problem
    PythonOperator.partial(
        task_id="foo",
        python_callable=lambda x: "hi" if x else "bye",
    ).expand(op_args=False)
```

When I check for errors like `python dags/the_dag.py` I get the following error:
```
Traceback (most recent call last):
  File "/Users/matt/2022/03/30/dags/the_dag.py", line 13, in <module>
    PythonOperator.partial(
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 187, in expand
    validate_mapping_kwargs(self.operator_class, "expand", mapped_kwargs)
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 116, in validate_mapping_kwargs
    raise ValueError(error)
ValueError: PythonOperator.expand() got an unexpected type 'bool' for keyword argument op_args
Exception ignored in: <function OperatorPartial.__del__ at 0x10c63b1f0>
Traceback (most recent call last):
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 182, in __del__
    warnings.warn(f"{self!r} was never mapped!")
  File "/usr/local/Cellar/python@3.9/3.9.10/Frameworks/Python.framework/Versions/3.9/lib/python3.9/warnings.py", line 109, in _showwarnmsg
    sw(msg.message, msg.category, msg.filename, msg.lineno,
  File "/Users/matt/src/airflow/airflow/settings.py", line 115, in custom_show_warning
    from rich.markup import escape
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 982, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 925, in _find_spec
  File "<frozen importlib._bootstrap_external>", line 1414, in find_spec
  File "<frozen importlib._bootstrap_external>", line 1380, in _get_spec
TypeError: 'NoneType' object is not iterable
```

### What you think should happen instead

I'm not sure what's up with that type error, the ValueError is what I needed to see.  So I expected this:

```
Traceback (most recent call last):
  File "/Users/matt/2022/03/30/dags/the_dag.py", line 13, in <module>
    PythonOperator.partial(
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 187, in expand
    validate_mapping_kwargs(self.operator_class, "expand", mapped_kwargs)
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 116, in validate_mapping_kwargs
    raise ValueError(error)
ValueError: PythonOperator.expand() got an unexpected type 'bool' for keyword argument op_args
```

### How to reproduce

_No response_

### Operating System

Mac OS

### Versions of Apache Airflow Providers

n/a

### Deployment

Virtualenv installation

### Deployment details

- cloned main at 327eab3e2
- created fresh venv and used pip to install
- `airflow info`
- `airflow db init`
- add the dag
- `python dags/the_dag.py`


### Anything else

_No response_

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)



Discussion:
@uranusjr suggested this change:

```
--- a/airflow/settings.py
+++ b/airflow/settings.py
@@ -120,7 +120,14 @@ def custom_show_warning(message, category, filename, lineno, file=None, line=Non
     write_console.print(msg, soft_wrap=True)


-warnings.showwarning = custom_show_warning
+def replace_showwarning(replacement):
+    original = warnings.showwarning
+    warnings.showwarning = replacement
+    return original
+
+
+original_show_warning = replace_showwarning(custom_show_warning)
+atexit.register(functools.partial(replace_showwarning, original_show_warning))
```

which altered the output, but didn't suppress it:

```
Traceback (most recent call last):
  File "/Users/matt/2022/03/30/dags/the_dag.py", line 13, in <module>
    PythonOperator.partial(
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 187, in expand
    validate_mapping_kwargs(self.operator_class, "expand", mapped_kwargs)
  File "/Users/matt/src/airflow/airflow/models/mappedoperator.py", line 116, in validate_mapping_kwargs
    raise ValueError(error)
ValueError: PythonOperator.expand() got an unexpected type 'bool' for keyword argument op_args
/Users/matt/src/airflow/airflow/models/mappedoperator.py:182: UserWarning: PythonOperator.partial(python_callable=<function <lambda> at 0x10db770d0>, dag=<DAG: singleton_expanded>, task_group=<airflow.utils.task_group.TaskGroup object at 0x1105c8b20>, task_id='foo', start_date=None, end_date=None, owner='airflow', email=None, params=<airflow.models.param.ParamsDict object at 0x10dcb5fd0>, trigger_rule=<TriggerRule.ALL_SUCCESS: 'all_success'>, depends_on_past=False, wait_for_downstream=False, retries=0, queue='default', pool='default_pool', pool_slots=1, execution_timeout=None, retry_delay=datetime.timedelta(seconds=300), retry_exponential_backoff=False, priority_weight=1, weight_rule=<WeightRule.DOWNSTREAM: 'downstream'>, sla=None, max_active_tis_per_dag=None, on_execute_callback=None, on_failure_callback=None, on_retry_callback=None, on_success_callback=None, run_as_user=None, executor_config={}, inlets=None, outlets=None, resources=None) was never mapped!
  warnings.warn(f"{self!r} was never mapped!")
```

Resolving patch:
diff --git a/airflow/models/mappedoperator.py b/airflow/models/mappedoperator.py
--- a/airflow/models/mappedoperator.py
+++ b/airflow/models/mappedoperator.py
@@ -179,9 +179,15 @@ def __repr__(self) -> str:
 
     def __del__(self):
         if not self._expand_called:
-            warnings.warn(f"{self!r} was never mapped!")
+            try:
+                task_id = repr(self.kwargs["task_id"])
+            except KeyError:
+                task_id = f"at {hex(id(self))}"
+            warnings.warn(f"Task {task_id} was never mapped!")
 
     def expand(self, **mapped_kwargs: "Mappable") -> "MappedOperator":
+        self._expand_called = True
+
         from airflow.operators.dummy import DummyOperator
 
         validate_mapping_kwargs(self.operator_class, "expand", mapped_kwargs)
@@ -218,7 +224,6 @@ def expand(self, **mapped_kwargs: "Mappable") -> "MappedOperator":
             start_date=start_date,
             end_date=end_date,
         )
-        self._expand_called = True
         return op
 
 


Test patch: