**Description**

Similarly to `@dag` and `@task` decorators, we can add a decorator to easily generate `TaskGroup` instances. 

**Use case / motivation**

This would be used for reusable pieces of DAGs. An example may be to hyperparameter tune an ML pipeline. You may want to generate several parallel subpipelines with different settings/parameters to be executed in parallel. At the end pull the result and decide the best parametrization.  

```py
@task
collect_dataset(...)
  pass

@task
def train_model(...)
   pass

@task_group
def movielens_model_pipeline(learning_rate: int, feature_list: list, dataset: XComArg):  
  dataset = filter_dataset(dataset, feature_list)
  train_model(dataset, learning_rate)

@dag
def movielens_hpt(dataset_path: str, feature_list:list=['year', 'director']):
  dataset = load_dataset(dataset_path)
  for i in range(0.1, 0.9, step=0.1):
    movielens_model_pipeline(i, feature_list, dataset)
  
  decide_best_model(...)
```




**Related Issues**

https://github.com/apache/airflow/issues?q=is%3Aopen+is%3Aissue+label%3AAIP-31


airflow/utils/task_group.py
"""TaskGroup context is used to keep the current TaskGroup when TaskGroup is used as ContextManager."""
    _context_managed_task_group: Optional[TaskGroup] = None
    _previous_context_managed_task_groups: List[TaskGroup] = []
    @classmethod
    def push_context_managed_task_group(cls, task_group: TaskGroup):
        """Push a TaskGroup into the list of managed TaskGroups."""
        if cls._context_managed_task_group:
            cls._previous_context_managed_task_groups.append(cls._context_managed_task_group)
        cls._context_managed_task_group = task_group
    @classmethod
    def pop_context_managed_task_group(cls) -> Optional[TaskGroup]:
        """Pops the last TaskGroup from the list of manged TaskGroups and update the current TaskGroup."""
        old_task_group = cls._context_managed_task_group
        if cls._previous_context_managed_task_groups:
            cls._context_managed_task_group = cls._previous_context_managed_task_groups.pop()
        else:
            cls._context_managed_task_group = None
        return old_task_group
    @classmethod
    def get_current_task_group(cls, dag: Optional["DAG"]) -> Optional[TaskGroup]:
        """Get the current TaskGroup."""
        from airflow.models.dag import DagContext
        if not cls._context_managed_task_group:
            dag = dag or DagContext.get_current_dag()
            if dag:
                
                return dag.task_group
        return cls._context_managed_task_group