Copilot commented on code in PR #64018:
URL: https://github.com/apache/airflow/pull/64018#discussion_r3066493679
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -21,6 +21,8 @@ Creating Custom ``@task`` Decorators
As of Airflow 2.2 it is possible add custom decorators to the TaskFlow
interface from within a provider
package and have those decorators appear natively as part of the
``@task.____`` design.
+Custom task decorators allow you to create specialized decorators that wrap
your functions with specific functionality. This is useful when you want to
create domain-specific decorators (e.g., ``@task.django`` for Django projects)
or provide pre-configured environments for your tasks.
+
For an example. Let's say you were trying to create an easier mechanism to run
python functions as "foo"
Review Comment:
This sentence is grammatically incorrect/awkward (“For an example.”).
Consider rephrasing to “For example, let’s say…” and capitalizing “Python” in
this context.
```suggestion
For example, let's say you were trying to create an easier mechanism to run
Python functions as "foo"
```
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -21,6 +21,8 @@ Creating Custom ``@task`` Decorators
As of Airflow 2.2 it is possible add custom decorators to the TaskFlow
interface from within a provider
Review Comment:
Grammar issue: this reads as though a word is missing. Consider changing to
“it is possible to add custom decorators…” for clarity.
```suggestion
As of Airflow 2.2 it is possible to add custom decorators to the TaskFlow
interface from within a provider
```
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -86,6 +98,65 @@ tasks. The steps to create and register ``@task.foo`` are:
Please note that the ``name`` must be a valid python identifier.
+Alternative: Creating Decorators for Local Projects
+---------------------------------------------------
+
+If you want to create custom decorators for use within a single Airflow
project without creating a full provider package,
+you can use a simpler factory approach. This is useful for project-specific
decorators like Django integration.
+
+Example: Creating a Django task decorator:
+
+.. code-block:: python
+
+ from __future__ import annotations
+ from pathlib import Path
+ from typing import Callable
+ from airflow.sdk import task
Review Comment:
This import path may not match the public TaskFlow import used in most
Airflow docs (`from airflow.decorators import task`). If `airflow.sdk` is
intentional (and version-dependent), consider clarifying the required
Airflow/version context; otherwise, use the standard public import to avoid
example breakage.
```suggestion
from airflow.decorators import task
```
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -86,6 +98,65 @@ tasks. The steps to create and register ``@task.foo`` are:
Please note that the ``name`` must be a valid python identifier.
+Alternative: Creating Decorators for Local Projects
+---------------------------------------------------
+
+If you want to create custom decorators for use within a single Airflow
project without creating a full provider package,
+you can use a simpler factory approach. This is useful for project-specific
decorators like Django integration.
+
+Example: Creating a Django task decorator:
+
+.. code-block:: python
+
+ from __future__ import annotations
+ from pathlib import Path
+ from typing import Callable
+ from airflow.sdk import task
+
+ def django_connect(app_path: Path, settings_module: str):
+ """Connect to Django database - implement your Django setup here"""
+ import os
+ import django
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)
+ django.setup()
+
+ def make_django_decorator(app_path: Path, settings_module: str):
+ """Factory function to create a Django-specific task decorator"""
+
+ def django_task(task_id: str = None, *args, **kwargs):
+ def django_task_decorator(fn: Callable):
+ @task(task_id=task_id or fn.__name__, *args, **kwargs)
Review Comment:
This is invalid Python syntax: starred positional expansion (`*args`) cannot
come after a keyword argument in a call. Reorder arguments (e.g., place `*args`
before `task_id=...`, or pass `task_id` via `kwargs`) so the example is
runnable.
```suggestion
@task(*args, task_id=task_id or fn.__name__, **kwargs)
```
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -86,6 +98,65 @@ tasks. The steps to create and register ``@task.foo`` are:
Please note that the ``name`` must be a valid python identifier.
+Alternative: Creating Decorators for Local Projects
+---------------------------------------------------
+
+If you want to create custom decorators for use within a single Airflow
project without creating a full provider package,
+you can use a simpler factory approach. This is useful for project-specific
decorators like Django integration.
+
+Example: Creating a Django task decorator:
+
+.. code-block:: python
+
+ from __future__ import annotations
+ from pathlib import Path
+ from typing import Callable
+ from airflow.sdk import task
+
+ def django_connect(app_path: Path, settings_module: str):
+ """Connect to Django database - implement your Django setup here"""
+ import os
+ import django
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)
+ django.setup()
+
+ def make_django_decorator(app_path: Path, settings_module: str):
+ """Factory function to create a Django-specific task decorator"""
+
+ def django_task(task_id: str = None, *args, **kwargs):
Review Comment:
Type hinting: `task_id` defaults to `None` but is annotated as `str`.
Consider annotating it as optional (e.g., `str | None`), which aligns with type
checkers and the `from __future__ import annotations` already used in the
snippet.
```suggestion
def django_task(task_id: str | None = None, *args, **kwargs):
```
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -86,6 +98,65 @@ tasks. The steps to create and register ``@task.foo`` are:
Please note that the ``name`` must be a valid python identifier.
+Alternative: Creating Decorators for Local Projects
+---------------------------------------------------
+
+If you want to create custom decorators for use within a single Airflow
project without creating a full provider package,
+you can use a simpler factory approach. This is useful for project-specific
decorators like Django integration.
+
+Example: Creating a Django task decorator:
+
+.. code-block:: python
+
+ from __future__ import annotations
+ from pathlib import Path
+ from typing import Callable
+ from airflow.sdk import task
+
+ def django_connect(app_path: Path, settings_module: str):
+ """Connect to Django database - implement your Django setup here"""
+ import os
+ import django
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)
+ django.setup()
+
+ def make_django_decorator(app_path: Path, settings_module: str):
+ """Factory function to create a Django-specific task decorator"""
+
+ def django_task(task_id: str = None, *args, **kwargs):
+ def django_task_decorator(fn: Callable):
+ @task(task_id=task_id or fn.__name__, *args, **kwargs)
+ def new_fn(*fn_args, **fn_kwargs):
+ # Connect to Django database before running the function
+ django_connect(app_path, settings_module)
+ # Now the function can use Django models
+ return fn(*fn_args, **fn_kwargs)
+ return new_fn
+ return django_task_decorator
+ return django_task
+
+Usage in your DAG:
+
+.. code-block:: python
+
+ from plugins.django_decorator import make_django_decorator
+ from pathlib import Path
+
+ # Create a Django-specific task decorator for your project
+ django_task = make_django_decorator(
+ app_path=Path("/path/to/your/django/project"),
+ settings_module="myproject.settings"
+ )
+
+ @django_task
Review Comment:
The example usage `@django_task` does not match the implementation:
`django_task` as written returns a decorator only when *called* (e.g.,
`@django_task()` or `@django_task(task_id=...)`). As-is, `@django_task` will
treat the function object as the `task_id` argument and break. Update the
example usage or adjust `django_task` to support being used both with and
without parentheses.
```suggestion
@django_task()
```
##########
airflow-core/docs/howto/create-custom-decorator.rst:
##########
@@ -86,6 +98,65 @@ tasks. The steps to create and register ``@task.foo`` are:
Please note that the ``name`` must be a valid python identifier.
+Alternative: Creating Decorators for Local Projects
+---------------------------------------------------
+
+If you want to create custom decorators for use within a single Airflow
project without creating a full provider package,
+you can use a simpler factory approach. This is useful for project-specific
decorators like Django integration.
+
+Example: Creating a Django task decorator:
+
+.. code-block:: python
+
+ from __future__ import annotations
+ from pathlib import Path
+ from typing import Callable
+ from airflow.sdk import task
+
+ def django_connect(app_path: Path, settings_module: str):
+ """Connect to Django database - implement your Django setup here"""
+ import os
+ import django
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)
+ django.setup()
+
+ def make_django_decorator(app_path: Path, settings_module: str):
+ """Factory function to create a Django-specific task decorator"""
+
+ def django_task(task_id: str = None, *args, **kwargs):
Review Comment:
The example usage `@django_task` does not match the implementation:
`django_task` as written returns a decorator only when *called* (e.g.,
`@django_task()` or `@django_task(task_id=...)`). As-is, `@django_task` will
treat the function object as the `task_id` argument and break. Update the
example usage or adjust `django_task` to support being used both with and
without parentheses.
--
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]