amoghrajesh opened a new pull request, #46176:
URL: https://github.com/apache/airflow/pull/46176

   <!--
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at
   
      http://www.apache.org/licenses/LICENSE-2.0
   
    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
    -->
   
   <!--
   Thank you for contributing! Please make sure that your code changes
   are covered with tests. And in case of new features or big changes
   remember to adjust the documentation.
   
   Feel free to ping committers for the review!
   
   In case of an existing issue, reference it using one of the following:
   
   closes: #ISSUE
   related: #ISSUE
   
   How to write a good git commit message:
   http://chris.beams.io/posts/git-commit/
   -->
   closes: #44361
   
   ### Why?
   DAG Params are needed to provide runtime information to tasks: 
https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/params.html.
 They are generally useful when you want to provide a per DAG Run level 
configuration for a dag run, lets say some dynamic information.
   
   These work with legacy Airflow 2 and should be ported over to task sdk.
   - The dag params classes from models have been ported over to the task sdk 
under `definitions` along with changing tests, references, documentation 
(atleast most of it), and general refs.
   - The existing tests continue to run
   
   
   ### Testing
   
   #### Basic Testing
   DAG used for testing:
   ```
   from airflow import DAG
   from airflow.decorators import task
   from airflow.sdk import Param
   
   with DAG(
       "param_dag",
       params={
           "x": Param(5, type="integer", minimum=3),
           "my_int_param": 6
       },
   ) as dag:
   
       @task
       def example_task():
           # This will print the default value, 6:
           print(dag.params['my_int_param'])
   
   
       example_task()
   ```
   
   
![image](https://github.com/user-attachments/assets/55d96bf7-10c7-4c89-a422-1e435b69a6ee)
   
   
   #### Params Trigger UI from example dags
   ```
   from __future__ import annotations
   
   import datetime
   from pathlib import Path
   
   from airflow.decorators import task
   from airflow.models.dag import DAG
   from airflow.sdk.definitions.param import Param, ParamsDict
   from airflow.utils.trigger_rule import TriggerRule
   
   # [START params_trigger]
   with DAG(
       dag_id=Path(__file__).stem,
       dag_display_name="Params Trigger UI",
       description=__doc__.partition(".")[0],
       doc_md=__doc__,
       schedule=None,
       start_date=datetime.datetime(2022, 3, 4),
       catchup=False,
       tags=["example", "params"],
       params={
           "names": Param(
               ["Linda", "Martha", "Thomas"],
               type="array",
               description="Define the list of names for which greetings should 
be generated in the logs."
               " Please have one name per line.",
               title="Names to greet",
           ),
           "english": Param(True, type="boolean", title="English"),
           "german": Param(True, type="boolean", title="German (Formal)"),
           "french": Param(True, type="boolean", title="French"),
       },
   ) as dag:
   
       @task(task_id="get_names", task_display_name="Get names")
       def get_names(**kwargs) -> list[str]:
           params: ParamsDict = kwargs["params"]
           if "names" not in params:
               print("Uuups, no names given, was no UI used to trigger?")
               return []
           return params["names"]
   
       @task.branch(task_id="select_languages", task_display_name="Select 
languages")
       def select_languages(**kwargs) -> list[str]:
           params: ParamsDict = kwargs["params"]
           selected_languages = []
           for lang in ["english", "german", "french"]:
               if params[lang]:
                   selected_languages.append(f"generate_{lang}_greeting")
           return selected_languages
   
       @task(task_id="generate_english_greeting", task_display_name="Generate 
English greeting")
       def generate_english_greeting(name: str) -> str:
           return f"Hello {name}!"
   
       @task(task_id="generate_german_greeting", task_display_name="Erzeuge 
Deutsche Begrüßung")
       def generate_german_greeting(name: str) -> str:
           return f"Sehr geehrter Herr/Frau {name}."
   
       @task(task_id="generate_french_greeting", task_display_name="Produire un 
message d'accueil en français")
       def generate_french_greeting(name: str) -> str:
           return f"Bonjour {name}!"
   
       @task(task_id="print_greetings", task_display_name="Print greetings", 
trigger_rule=TriggerRule.ALL_DONE)
       def print_greetings(greetings1, greetings2, greetings3) -> None:
           for g in greetings1 or []:
               print(g)
           for g in greetings2 or []:
               print(g)
           for g in greetings3 or []:
               print(g)
           if not (greetings1 or greetings2 or greetings3):
               print("sad, nobody to greet :-(")
   
       lang_select = select_languages()
       names = get_names()
       english_greetings = generate_english_greeting.expand(name=names)
       german_greetings = generate_german_greeting.expand(name=names)
       french_greetings = generate_french_greeting.expand(name=names)
       lang_select >> [english_greetings, german_greetings, french_greetings]
       results_print = print_greetings(english_greetings, german_greetings, 
french_greetings)
   # [END params_trigger]
   ```
   
   <img width="1723" alt="image" 
src="https://github.com/user-attachments/assets/2ce09a68-b21c-47eb-89e9-9b077b2c26ca";
 />
   
   <img width="1723" alt="image" 
src="https://github.com/user-attachments/assets/9236f4e3-7738-497b-85ad-a0be6490d2c5";
 />
   
   <img width="1723" alt="image" 
src="https://github.com/user-attachments/assets/1a1aba71-1036-4db5-a7db-3ff031cb1ae9";
 />
   
   The failures above are because mappedtasks dont work yet
   
   #### Params UI tutorial DAG demonstrating various options for a trigger form 
generated by DAG params
   
   Run config:
   ```
   {
       "a_simple_list": [
           "one",
           "two",
           "three",
           "actually one value is made per line"
       ],
       "array_of_numbers": [
           1,
           2,
           3
       ],
       "array_of_objects": [
           {
               "country": "country_name",
               "name": "account_name"
           }
       ],
       "bool": true,
       "checked_number": 100,
       "checked_text": "length-checked-field",
       "date": "2025-01-27",
       "date_time": "2025-01-27T12:17:00+00:00",
       "flag": false,
       "hidden_secret_field": "constant value",
       "most_loved_number": 42,
       "multi_select": [
           "two",
           "three"
       ],
       "multi_select_with_label": [
           "2",
           "3"
       ],
       "multiline_text": "A multiline text Param\nthat will keep the 
newline\ncharacters in its value.",
       "object": {
           "key": "value"
       },
       "optional_field": "optional text, you can trigger also w/o text",
       "pick_one": "value 42",
       "pick_with_label": 3,
       "proposals": "Alpha",
       "required_field": "dummy dummy",
       "text": "Hello World!",
       "time": "12:13:14",
       "x": 3
   }
   ```
   
   <img width="1723" alt="image" 
src="https://github.com/user-attachments/assets/2b8bdc58-b95d-4e02-a88a-86c6f8458201";
 />
   
   Logs:
   ```
   beef66d4770b
    ▶ Log message source details
   
{"logger":"airflow.dag_processing.bundles.manager.DagBundlesManager","timestamp":"2025-01-27T15:26:19.910440","event":"DAG
 bundles loaded: dags-folder, example_dags","level":"info"}
   
{"logger":"airflow.models.dagbag.DagBag","timestamp":"2025-01-27T15:26:19.910732","event":"Filling
 up the DagBag from 
/opt/airflow/airflow/example_dags/example_params_ui_tutorial.py","level":"info"}
   
{"logger":"airflow.models.dagbag.DagBag","timestamp":"2025-01-27T15:26:19.911277","event":"Importing
 
/opt/airflow/airflow/example_dags/example_params_ui_tutorial.py","level":"debug"}
   
{"logger":"airflow.models.dagbag.DagBag","timestamp":"2025-01-27T15:26:19.946419","event":"Loaded
 DAG <DAG: example_params_ui_tutorial>","level":"debug"}
   
{"file":"/opt/airflow/airflow/example_dags/example_params_ui_tutorial.py","timestamp":"2025-01-27T15:26:19.946663","logger":"task","event":"DAG
 file parsed","level":"debug"}
   
{"json":"{\"rendered_fields\":{\"templates_dict\":null,\"op_args\":\"()\",\"op_kwargs\":{}},\"type\":\"SetRenderedFields\"}\n","timestamp":"2025-01-27T15:26:19.946922","logger":"task","event":"Sending
 request","level":"debug"}
   
{"logger":"airflow.sdk.definitions.param","timestamp":"2025-01-27T15:26:19.956861","event":"Updating
 task params ({'x': 3, 'text': 'Hello World!', 'flag': False, 'a_simple_list': 
['one', 'two', 'three', 'actually one value is made per line'], 
'most_loved_number': 42, 'pick_one': 'value 42', 'pick_with_label': 3, 
'proposals': 'Alpha', 'multi_select': ['two', 'three'], 
'multi_select_with_label': ['2', '3'], 'array_of_numbers': [1, 2, 3], 'bool': 
True, 'date_time': '2025-01-27T12:17:00+00:00', 'date': '2025-01-27', 'time': 
'12:13:14', 'multiline_text': 'A multiline text Param\\nthat will keep the 
newline\\ncharacters in its value.', 'required_field': None, 'optional_field': 
'optional text, you can trigger also w/o text', 'checked_text': 
'length-checked-field', 'checked_number': 100, 'object': {'key': 'value'}, 
'array_of_objects': [{'name': 'account_name', 'country': 'country_name'}], 
'hidden_secret_field': 'constant value'}) with DagRun.conf ({'x': 3, 'bool': 
True, 'date': '2025-01-2
 7', 'flag': False, 'text': 'Hello World!', 'time': '12:13:14', 'object': 
{'key': 'value'}, 'pick_one': 'value 42', 'date_time': 
'2025-01-27T12:17:00+00:00', 'proposals': 'Alpha', 'checked_text': 
'length-checked-field', 'multi_select': ['two', 'three'], 'a_simple_list': 
['one', 'two', 'three', 'actually one value is made per line'], 
'checked_number': 100, 'multiline_text': 'A multiline text Param\\nthat will 
keep the newline\\ncharacters in its value.', 'optional_field': 'optional text, 
you can trigger also w/o text', 'required_field': 'dummy dummy', 
'pick_with_label': 3, 'array_of_numbers': [1, 2, 3], 'array_of_objects': 
[{'name': 'account_name', 'country': 'country_name'}], 'most_loved_number': 42, 
'hidden_secret_field': 'constant value', 'multi_select_with_label': ['2', 
'3']})","level":"debug"}
   {"chan":"stdout","event":"Validates params aere {'x': 3, 'text': 'Hello 
World!', 'flag': False, 'a_simple_list': ['one', 'two', 'three', 'actually one 
value is made per line'], 'most_loved_number': 42, 'pick_one': 'value 42', 
'pick_with_label': 3, 'proposals': 'Alpha', 'multi_select': ['two', 'three'], 
'multi_select_with_label': ['2', '3'], 'array_of_numbers': [1, 2, 3], 'bool': 
True, 'date_time': '2025-01-27T12:17:00+00:00', 'date': '2025-01-27', 'time': 
'12:13:14', 'multiline_text': 'A multiline text Param\\nthat will keep the 
newline\\ncharacters in its value.', 'required_field': 'dummy dummy', 
'optional_field': 'optional text, you can trigger also w/o text', 
'checked_text': 'length-checked-field', 'checked_number': 100, 'object': 
{'key': 'value'}, 'array_of_objects': [{'name': 'account_name', 'country': 
'country_name'}], 'hidden_secret_field': 'constant 
value'}","timestamp":"2025-01-27T15:26:19.967251Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"This DAG was triggered with the following 
parameters:","timestamp":"2025-01-27T15:26:19.971408Z","level":"info","logger":"task"}
   
{"chan":"stdout","event":"","timestamp":"2025-01-27T15:26:19.971505Z","level":"info","logger":"task"}
   
{"chan":"stdout","event":"{","timestamp":"2025-01-27T15:26:19.971657Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"x\": 
3,","timestamp":"2025-01-27T15:26:19.971733Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"text\": \"Hello 
World!\",","timestamp":"2025-01-27T15:26:19.971813Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"flag\": 
false,","timestamp":"2025-01-27T15:26:19.971885Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"a_simple_list\": 
[","timestamp":"2025-01-27T15:26:19.971956Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"one\",","timestamp":"2025-01-27T15:26:19.972028Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"two\",","timestamp":"2025-01-27T15:26:19.972097Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"three\",","timestamp":"2025-01-27T15:26:19.972166Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        \"actually one value is made per 
line\"","timestamp":"2025-01-27T15:26:19.972238Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    
],","timestamp":"2025-01-27T15:26:19.972313Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"most_loved_number\": 
42,","timestamp":"2025-01-27T15:26:19.972383Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"pick_one\": \"value 
42\",","timestamp":"2025-01-27T15:26:19.972453Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"pick_with_label\": 
3,","timestamp":"2025-01-27T15:26:19.972524Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"proposals\": 
\"Alpha\",","timestamp":"2025-01-27T15:26:19.972593Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"multi_select\": 
[","timestamp":"2025-01-27T15:26:19.972663Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"two\",","timestamp":"2025-01-27T15:26:19.972734Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"three\"","timestamp":"2025-01-27T15:26:19.972806Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    
],","timestamp":"2025-01-27T15:26:19.972875Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"multi_select_with_label\": 
[","timestamp":"2025-01-27T15:26:19.972948Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"2\",","timestamp":"2025-01-27T15:26:19.973017Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
\"3\"","timestamp":"2025-01-27T15:26:19.973086Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    
],","timestamp":"2025-01-27T15:26:19.973165Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"array_of_numbers\": 
[","timestamp":"2025-01-27T15:26:19.973251Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
1,","timestamp":"2025-01-27T15:26:19.973324Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
2,","timestamp":"2025-01-27T15:26:19.973396Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
3","timestamp":"2025-01-27T15:26:19.973464Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    
],","timestamp":"2025-01-27T15:26:19.973534Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"bool\": 
true,","timestamp":"2025-01-27T15:26:19.973603Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"date_time\": 
\"2025-01-27T12:17:00+00:00\",","timestamp":"2025-01-27T15:26:19.973671Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"date\": 
\"2025-01-27\",","timestamp":"2025-01-27T15:26:19.973740Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"time\": 
\"12:13:14\",","timestamp":"2025-01-27T15:26:19.973807Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"multiline_text\": \"A multiline text 
Param\\nthat will keep the newline\\ncharacters in its 
value.\",","timestamp":"2025-01-27T15:26:19.973876Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"required_field\": \"dummy 
dummy\",","timestamp":"2025-01-27T15:26:19.973946Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"optional_field\": \"optional text, you can 
trigger also w/o 
text\",","timestamp":"2025-01-27T15:26:19.974077Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"checked_text\": 
\"length-checked-field\",","timestamp":"2025-01-27T15:26:19.974145Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"checked_number\": 
100,","timestamp":"2025-01-27T15:26:19.974212Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"object\": 
{","timestamp":"2025-01-27T15:26:19.974278Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        \"key\": 
\"value\"","timestamp":"2025-01-27T15:26:19.974350Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    
},","timestamp":"2025-01-27T15:26:19.974411Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"array_of_objects\": 
[","timestamp":"2025-01-27T15:26:19.974478Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
{","timestamp":"2025-01-27T15:26:19.974559Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"            \"name\": 
\"account_name\",","timestamp":"2025-01-27T15:26:19.974625Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"            \"country\": 
\"country_name\"","timestamp":"2025-01-27T15:26:19.974692Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"        
}","timestamp":"2025-01-27T15:26:19.974777Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    
],","timestamp":"2025-01-27T15:26:19.974844Z","level":"info","logger":"task"}
   {"chan":"stdout","event":"    \"hidden_secret_field\": \"constant 
value\"","timestamp":"2025-01-27T15:26:19.974911Z","level":"info","logger":"task"}
   
{"chan":"stdout","event":"}","timestamp":"2025-01-27T15:26:19.974980Z","level":"info","logger":"task"}
   
{"chan":"stdout","event":"","timestamp":"2025-01-27T15:26:19.975050Z","level":"info","logger":"task"}
   
{"logger":"airflow.task.operators.airflow.decorators.python._PythonDecoratedOperator","timestamp":"2025-01-27T15:26:19.971434","event":"Done.
 Returned value was: None","level":"info"}
   
{"json":"{\"state\":\"success\",\"end_date\":\"2025-01-27T15:26:19.971547Z\",\"task_outlets\":[],\"outlet_events\":[],\"type\":\"SucceedTask\"}\n","timestamp":"2025-01-27T15:26:19.971629","logger":"task","event":"Sending
 request","level":"debug"}
   ```
   
   
   
   <!-- Please keep an empty line above the dashes. -->
   ---
   **^ Add meaningful description above**
   Read the **[Pull Request 
Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)**
 for more information.
   In case of fundamental code changes, an Airflow Improvement Proposal 
([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals))
 is needed.
   In case of a new dependency, check compliance with the [ASF 3rd Party 
License Policy](https://www.apache.org/legal/resolved.html#category-x).
   In case of backwards incompatible changes please leave a note in a 
newsfragment file, named `{pr_number}.significant.rst` or 
`{issue_number}.significant.rst`, in 
[newsfragments](https://github.com/apache/airflow/tree/main/newsfragments).
   


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