Repository: apache/airflow

Issue:
When passing the 'False' value to the parameters of a decorated dag function I get this traceback
### Apache Airflow version

2.2.3

### What happened

When passing the `False` value to a decorated dag function I get this traceback below. Also the default value is not shown when clicking 'trigger dag w/ config'.


```[2022-04-07, 20:08:57 UTC] {taskinstance.py:1259} INFO - Executing <Task(_PythonDecoratedOperator): value_consumer> on 2022-04-07 20:08:56.914410+00:00
[2022-04-07, 20:08:57 UTC] {standard_task_runner.py:52} INFO - Started process 2170 to run task
[2022-04-07, 20:08:57 UTC] {standard_task_runner.py:76} INFO - Running: ['airflow', 'tasks', 'run', 'check_ui_config', 'value_consumer', 'manual__2022-04-07T20:08:56.914410+00:00', '--job-id', '24', '--raw', '--subdir', 'DAGS_FOLDER/check_ui_config.py', '--cfg-path', '/tmp/tmpww9euksv', '--error-file', '/tmp/tmp7kjdfks5']
[2022-04-07, 20:08:57 UTC] {standard_task_runner.py:77} INFO - Job 24: Subtask value_consumer
[2022-04-07, 20:08:57 UTC] {logging_mixin.py:109} INFO - Running <TaskInstance: check_ui_config.value_consumer manual__2022-04-07T20:08:56.914410+00:00 [running]> on host a643f8828615
[2022-04-07, 20:08:57 UTC] {taskinstance.py:1700} ERROR - Task failed with exception
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1329, in _run_raw_task
    self._execute_task_with_callbacks(context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1418, in _execute_task_with_callbacks
    self.render_templates(context=context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1992, in render_templates
    self.task.render_template_fields(context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1061, in render_template_fields
    self._do_render_template_fields(self, self.template_fields, context, jinja_env, set())
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1074, in _do_render_template_fields
    rendered_content = self.render_template(content, context, jinja_env, seen_oids)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1125, in render_template
    return tuple(self.render_template(element, context, jinja_env) for element in content)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1125, in <genexpr>
    return tuple(self.render_template(element, context, jinja_env) for element in content)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1116, in render_template
    return content.resolve(context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/param.py", line 226, in resolve
    raise AirflowException(f'No value could be resolved for parameter {self._name}')
airflow.exceptions.AirflowException: No value could be resolved for parameter test
[2022-04-07, 20:08:57 UTC] {taskinstance.py:1267} INFO - Marking task as FAILED. dag_id=check_ui_config, task_id=value_consumer, execution_date=20220407T200856, start_date=20220407T200857, end_date=20220407T200857
[2022-04-07, 20:08:57 UTC] {standard_task_runner.py:89} ERROR - Failed to execute job 24 for task value_consumer
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/airflow/task/task_runner/standard_task_runner.py", line 85, in _start_by_fork
    args.func(args, dag=self.dag)
  File "/usr/local/lib/python3.9/site-packages/airflow/cli/cli_parser.py", line 48, in command
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/airflow/utils/cli.py", line 92, in wrapper
    return f(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/airflow/cli/commands/task_command.py", line 298, in task_run
    _run_task_by_selected_method(args, dag, ti)
  File "/usr/local/lib/python3.9/site-packages/airflow/cli/commands/task_command.py", line 107, in _run_task_by_selected_method
    _run_raw_task(args, ti)
  File "/usr/local/lib/python3.9/site-packages/airflow/cli/commands/task_command.py", line 180, in _run_raw_task
    ti._run_raw_task(
  File "/usr/local/lib/python3.9/site-packages/airflow/utils/session.py", line 70, in wrapper
    return func(*args, session=session, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1329, in _run_raw_task
    self._execute_task_with_callbacks(context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1418, in _execute_task_with_callbacks
    self.render_templates(context=context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1992, in render_templates
    self.task.render_template_fields(context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1061, in render_template_fields
    self._do_render_template_fields(self, self.template_fields, context, jinja_env, set())
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1074, in _do_render_template_fields
    rendered_content = self.render_template(content, context, jinja_env, seen_oids)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1125, in render_template
    return tuple(self.render_template(element, context, jinja_env) for element in content)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1125, in <genexpr>
    return tuple(self.render_template(element, context, jinja_env) for element in content)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/baseoperator.py", line 1116, in render_template
    return content.resolve(context)
  File "/usr/local/lib/python3.9/site-packages/airflow/models/param.py", line 226, in resolve
    raise AirflowException(f'No value could be resolved for parameter {self._name}')
airflow.exceptions.AirflowException: No value could be resolved for parameter test
```

### What you think should happen instead

I think airflow should be able to handle the False value when passing it as a dag param.

### How to reproduce
```
from airflow.decorators import dag, task
from airflow.models.param import Param

from datetime import datetime, timedelta


@task
def value_consumer(val):
    print(val)


@dag(
    start_date=datetime(2021, 1, 1),
    schedule_interval=timedelta(days=365, hours=6)
)
def check_ui_config(test):
    value_consumer(test)
    

the_dag = check_ui_config(False)
```
### Operating System

Docker (debian:buster)

### Versions of Apache Airflow Providers

_No response_

### Deployment

Astronomer

### Deployment details

Astro cli with this image:
quay.io/astronomer/ap-airflow-dev:2.2.3-2

### Anything else

![Screenshot from 2022-04-07 14-13-43](https://user-images.githubusercontent.com/102494105/162288264-bb6c6ca6-977f-4ff7-a0cc-9616c0ce8ac8.png)


### 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:
This appears to be the case for any falsey value.  Consider this dag:

```python3
@dag
def paramstuff(x, a=Param(2, minimum=0)):

    @task
    def add(one, other):
        print(one + other)

    add(x, a)


the_dag = paramstuff(3)  # two params: x,a
# add says 5 unless you specify different params at run time

# the_dag = paramstuff(0)  # one dag param: a
# add says: AirflowException: No value could be resolved for parameter x
```

I'm not sure how we should treat positional args being passed into `@dag` decorated functions (I'm tempted to say that they shouldn't interact with DagParams at all, leave that to the kwargs).  But whatever we do, it probably shouldn't depend on whether those values are falsey.

Resolving patch:
diff --git a/airflow/models/dag.py b/airflow/models/dag.py
--- a/airflow/models/dag.py
+++ b/airflow/models/dag.py
@@ -985,7 +985,7 @@ def pickle_id(self) -> Optional[int]:
     def pickle_id(self, value: int) -> None:
         self._pickle_id = value
 
-    def param(self, name: str, default=None) -> DagParam:
+    def param(self, name: str, default: Any = NOTSET) -> DagParam:
         """
         Return a DagParam object for current dag.
 
diff --git a/airflow/models/param.py b/airflow/models/param.py
--- a/airflow/models/param.py
+++ b/airflow/models/param.py
@@ -14,15 +14,19 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+import contextlib
 import copy
 import json
 import warnings
-from typing import Any, Dict, ItemsView, MutableMapping, Optional, ValuesView
+from typing import TYPE_CHECKING, Any, Dict, ItemsView, MutableMapping, Optional, ValuesView
 
 from airflow.exceptions import AirflowException, ParamValidationError
 from airflow.utils.context import Context
 from airflow.utils.types import NOTSET, ArgNotSet
 
+if TYPE_CHECKING:
+    from airflow.models.dag import DAG
+
 
 class Param:
     """
@@ -228,18 +232,18 @@ class DagParam:
     :param default: Default value used if no parameter was set.
     """
 
-    def __init__(self, current_dag, name: str, default: Optional[Any] = None):
-        if default:
+    def __init__(self, current_dag: "DAG", name: str, default: Any = NOTSET):
+        if default is not NOTSET:
             current_dag.params[name] = default
         self._name = name
         self._default = default
 
     def resolve(self, context: Context) -> Any:
         """Pull DagParam value from DagRun context. This method is run during ``op.execute()``."""
-        default = self._default
-        if not self._default:
-            default = context['params'][self._name] if self._name in context['params'] else None
-        resolved = context['dag_run'].conf.get(self._name, default)
-        if not resolved:
-            raise AirflowException(f'No value could be resolved for parameter {self._name}')
-        return resolved
+        with contextlib.suppress(KeyError):
+            return context['dag_run'].conf[self._name]
+        if self._default is not NOTSET:
+            return self._default
+        with contextlib.suppress(KeyError):
+            return context['params'][self._name]
+        raise AirflowException(f'No value could be resolved for parameter {self._name}')


Test patch:
diff --git a/tests/models/test_dag.py b/tests/models/test_dag.py
--- a/tests/models/test_dag.py
+++ b/tests/models/test_dag.py
@@ -27,7 +27,7 @@
 from datetime import timedelta
 from pathlib import Path
 from tempfile import NamedTemporaryFile
-from typing import List, Optional, Sequence
+from typing import List, Optional
 from unittest import mock
 from unittest.mock import patch
 
@@ -499,7 +499,7 @@ def test_resolve_template_files_value(self):
                 task = DummyOperator(task_id='op1')
 
             task.test_field = template_file
-            task.template_fields: Sequence[str] = ('test_field',)
+            task.template_fields = ('test_field',)
             task.template_ext = ('.template',)
             task.resolve_template_files()
 
@@ -517,7 +517,7 @@ def test_resolve_template_files_list(self):
                 task = DummyOperator(task_id='op1')
 
             task.test_field = [template_file, 'some_string']
-            task.template_fields: Sequence[str] = ('test_field',)
+            task.template_fields = ('test_field',)
             task.template_ext = ('.template',)
             task.resolve_template_files()
 
@@ -2006,7 +2006,7 @@ def test_count_number_queries(self, tasks_count):
             )
 
 
-class TestDagDecorator(unittest.TestCase):
+class TestDagDecorator:
     DEFAULT_ARGS = {
         "owner": "test",
         "depends_on_past": True,
@@ -2017,12 +2017,10 @@ class TestDagDecorator(unittest.TestCase):
     DEFAULT_DATE = timezone.datetime(2016, 1, 1)
     VALUE = 42
 
-    def setUp(self):
-        super().setUp()
+    def setup_method(self):
         self.operator = None
 
-    def tearDown(self):
-        super().tearDown()
+    def teardown_method(self):
         clear_db_runs()
 
     def test_fileloc(self):
@@ -2118,6 +2116,7 @@ def return_num(num):
             run_id=DagRunType.MANUAL.value,
             start_date=timezone.utcnow(),
             execution_date=self.DEFAULT_DATE,
+            data_interval=(self.DEFAULT_DATE, self.DEFAULT_DATE),
             state=State.RUNNING,
         )
 
@@ -2145,6 +2144,7 @@ def return_num(num):
             run_id=DagRunType.MANUAL.value,
             start_date=timezone.utcnow(),
             execution_date=self.DEFAULT_DATE,
+            data_interval=(self.DEFAULT_DATE, self.DEFAULT_DATE),
             state=State.RUNNING,
             conf={'value': new_value},
         )
@@ -2153,11 +2153,12 @@ def return_num(num):
         ti = dr.get_task_instances()[0]
         assert ti.xcom_pull(), new_value
 
-    def test_set_params_for_dag(self):
+    @pytest.mark.parametrize("value", [VALUE, 0])
+    def test_set_params_for_dag(self, value):
         """Test that dag param is correctly set when using dag decorator"""
 
         @dag_decorator(default_args=self.DEFAULT_ARGS)
-        def xcom_pass_to_op(value=self.VALUE):
+        def xcom_pass_to_op(value=value):
             @task_decorator
             def return_num(num):
                 return num
@@ -2166,7 +2167,7 @@ def return_num(num):
             self.operator = xcom_arg.operator
 
         dag = xcom_pass_to_op()
-        assert dag.params['value'] == self.VALUE
+        assert dag.params['value'] == value
 
 
 @pytest.mark.parametrize("run_id, execution_date", [(None, datetime_tz(2020, 1, 1)), ('test-run-id', None)])