Skip to content

Commit e6c61ca

Browse files
authored
Add date format filters to Jinja environment (#17451)
On its own this doesn't add all that much, but this is preparatory work to be combined with the new data_interval_start template context variables we are adding, without having to add the ds/ts/no-dash etc permutations of all of them.
1 parent 2ed5784 commit e6c61ca

File tree

10 files changed

+91
-20
lines changed

10 files changed

+91
-20
lines changed

airflow/macros/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
# KIND, either express or implied. See the License for the
1616
# specific language governing permissions and limitations
1717
# under the License.
18-
"""Macros."""
1918
import time # noqa
2019
import uuid # noqa
2120
from datetime import datetime, timedelta

airflow/templates.py

+34
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
class SandboxedEnvironment(jinja2.sandbox.SandboxedEnvironment):
2323
"""SandboxedEnvironment for Airflow task templates."""
2424

25+
def __init__(self, **kwargs):
26+
super().__init__(**kwargs)
27+
28+
self.filters.update(FILTERS)
29+
2530
def is_safe_attribute(self, obj, attr, value):
2631
"""
2732
Allow access to ``_`` prefix vars (but not ``__``).
@@ -30,3 +35,32 @@ def is_safe_attribute(self, obj, attr, value):
3035
``_``) whilst still blocking internal or truely private attributes (``__`` prefixed ones).
3136
"""
3237
return not jinja2.sandbox.is_internal_attribute(obj, attr)
38+
39+
40+
def ds_filter(value):
41+
return value.strftime('%Y-%m-%d')
42+
43+
44+
def ds_nodash_filter(value):
45+
return value.strftime('%Y%m%d')
46+
47+
48+
def ts_filter(value):
49+
return value.isoformat()
50+
51+
52+
def ts_nodash_filter(value):
53+
return value.strftime('%Y%m%dT%H%M%S')
54+
55+
56+
def ts_nodash_with_tz_filter(value):
57+
return value.isoformat().replace('-', '').replace(':', '')
58+
59+
60+
FILTERS = {
61+
'ds': ds_filter,
62+
'ds_nodash': ds_nodash_filter,
63+
'ts': ts_filter,
64+
'ts_nodash': ts_nodash_filter,
65+
'ts_nodash_with_tz': ts_nodash_with_tz_filter,
66+
}

docs/apache-airflow/concepts/operators.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ But there are many, many more - you can see the list of those in our :doc:`apach
5858

5959
Jinja Templating
6060
----------------
61-
Airflow leverages the power of `Jinja Templating <http://jinja.pocoo.org/docs/dev/>`_ and this can be a powerful tool to use in combination with :doc:`macros </macros-ref>`.
61+
Airflow leverages the power of `Jinja Templating <http://jinja.pocoo.org/docs/dev/>`_ and this can be a powerful tool to use in combination with :ref:`macros <templates-ref>`.
6262

6363
For example, say you want to pass the execution date as an environment variable to a Bash script using the ``BashOperator``:
6464

@@ -73,7 +73,7 @@ For example, say you want to pass the execution date as an environment variable
7373
env={"EXECUTION_DATE": date},
7474
)
7575
76-
Here, ``{{ ds }}`` is a macro, and because the ``env`` parameter of the ``BashOperator`` is templated with Jinja, the execution date will be available as an environment variable named ``EXECUTION_DATE`` in your Bash script.
76+
Here, ``{{ ds }}`` is a templated variable, and because the ``env`` parameter of the ``BashOperator`` is templated with Jinja, the execution date will be available as an environment variable named ``EXECUTION_DATE`` in your Bash script.
7777

7878
You can use Jinja templating with every parameter that is marked as "templated" in the documentation. Template substitution occurs just before the ``pre_execute`` function of your operator is called.
7979

docs/apache-airflow/faq.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ Airflow was developed as a solution for ETL needs. In the ETL world, you typical
220220
summarize data for 2016-02-19, You would do it at 2016-02-20 midnight UTC, which would be right after all data for
221221
2016-02-19 becomes available.
222222

223-
This datetime value is available to you as :ref:`Macros<macros:default_variables>` as various forms in Jinja templated
223+
This datetime value is available to you as :ref:`Template variables<templates:variables>` with various formats in Jinja templated
224224
fields. They are also included in the context dictionary given to an Operator's execute function.
225225

226226
.. code-block:: python

docs/apache-airflow/howto/operator/python.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Templating
4747
^^^^^^^^^^
4848

4949
Airflow passes in an additional set of keyword arguments: one for each of the
50-
:doc:`Jinja template variables <../../macros-ref>` and a ``templates_dict``
50+
:ref:`Jinja template variables <templates:variables>` and a ``templates_dict``
5151
argument.
5252

5353
The ``templates_dict`` argument is templated, so each value in the dictionary

docs/apache-airflow/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ unit of work and continuity.
110110

111111
Operators and hooks <operators-and-hooks-ref>
112112
CLI <cli-and-env-variables-ref>
113-
Macros <macros-ref>
113+
Templates <templates-ref>
114114
Python API <python-api-ref>
115115
Stable REST API <stable-rest-api-ref>
116116
deprecated-rest-api-ref

docs/apache-airflow/redirects.txt

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ start.rst start/index.rst
4444
cli-ref.rst cli-and-env-variables-ref.rst
4545
_api/index.rst python-api-ref.rst
4646
rest-api-ref.rst deprecated-rest-api-ref.rst
47+
macros-ref.rst templates-ref.rst
4748

4849
# Concepts
4950
concepts.rst concepts/index.rst

docs/apache-airflow/macros-ref.rst docs/apache-airflow/templates-ref.rst

+33-13
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@
1515
specific language governing permissions and limitations
1616
under the License.
1717
18-
.. _macros:
18+
.. _templates-ref:
1919

20-
Macros reference
21-
================
20+
Templates reference
21+
===================
2222

23-
Variables and macros can be used in templates (see the :ref:`concepts:jinja-templating` section)
23+
Variables, macros and filters can be used in templates (see the :ref:`concepts:jinja-templating` section)
2424

2525
The following come for free out of the box with Airflow.
2626
Additional custom macros can be added globally through :doc:`plugins`, or at a DAG level through the ``DAG.user_defined_macros`` argument.
2727

28-
.. _macros:default_variables:
28+
.. _templates:variables:
2929

30-
Default Variables
31-
-----------------
30+
Variables
31+
---------
3232
The Airflow engine passes a few variables by default that are accessible
3333
in all templates
3434

@@ -105,14 +105,35 @@ For example, you could use expressions in your templates like ``{{ conn.my_conn_
105105
Just like with ``var`` it's possible to fetch a connection by string (e.g. ``{{ conn.get('my_conn_id_'+index).host }}``
106106
) or provide defaults (e.g ``{{ conn.get('my_conn_id', {"host": "host1", "login": "user1"}).host }}``)
107107

108+
Filters
109+
-------
110+
111+
Airflow defines the some Jinja filters that can be used to format values.
112+
113+
For example, using ``{{ execution_date | ds }}`` will output the execution_date in the ``YYYY-MM-DD`` format.
114+
115+
===================== ============ ==================================================================
116+
Filter Operates on Description
117+
===================== ============ ==================================================================
118+
``ds`` datetime Format the datetime as ``YYYY-MM-DD``
119+
``ds_nodash`` datetime Format the datetime as ``YYYYMMDD``
120+
``ts`` datetime Same as ``.isoformat()``, Example: ``2018-01-01T00:00:00+00:00``
121+
``ts_nodash`` datetime Same as ``ts`` filter without ``-``, ``:`` or TimeZone info.
122+
Example: ``20180101T000000``
123+
``ts_nodash_with_tz`` datetime As ``ts`` filter without ``-`` or ``:``. Example
124+
``20180101T000000+0000``
125+
===================== ============ ==================================================================
126+
127+
128+
.. _templates:macros:
129+
108130
Macros
109131
------
110132
Macros are a way to expose objects to your templates and live under the
111133
``macros`` namespace in your templates.
112134

113135
A few commonly used libraries and methods are made available.
114136

115-
116137
================================= ==============================================
117138
Variable Description
118139
================================= ==============================================
@@ -124,13 +145,12 @@ Variable Description
124145
``macros.random`` The standard lib's :mod:`random`
125146
================================= ==============================================
126147

127-
128148
Some airflow specific macros are also defined:
129149

130150
.. automodule:: airflow.macros
131-
:show-inheritance:
132151
:members:
133152

134-
.. autofunction:: airflow.macros.hive.closest_ds_partition
135-
.. autofunction:: airflow.macros.hive.max_partition
136-
.. _pendulum.Pendulum: https://pendulum.eustace.io/docs/1.x/#introduction
153+
.. automodule:: airflow.macros.hive
154+
:members:
155+
156+
.. _pendulum.Pendulum: https://pendulum.eustace.io/docs/2.x/#introduction

docs/apache-airflow/tutorial.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ regarding custom filters have a look at the
178178
`Jinja Documentation <http://jinja.pocoo.org/docs/dev/api/#writing-filters>`_.
179179

180180
For more information on the variables and macros that can be referenced
181-
in templates, make sure to read through the :doc:`macros-ref`.
181+
in templates, make sure to read through the :ref:`templates-ref`.
182182

183183
Adding DAG and Tasks documentation
184184
----------------------------------

tests/core/test_templates.py

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import jinja2
1919
import jinja2.exceptions
20+
import pendulum
2021
import pytest
2122

2223
import airflow.templates
@@ -37,3 +38,19 @@ class Test:
3738
def test_private_access(env):
3839
with pytest.raises(jinja2.exceptions.SecurityError):
3940
env.from_string(r'{{ func.__code__ }}').render(func=test_private_access)
41+
42+
43+
@pytest.mark.parametrize(
44+
['name', 'expected'],
45+
(
46+
('ds', '2012-07-24'),
47+
('ds_nodash', '20120724'),
48+
('ts', '2012-07-24T03:04:52+00:00'),
49+
('ts_nodash', '20120724T030452'),
50+
('ts_nodash_with_tz', '20120724T030452+0000'),
51+
),
52+
)
53+
def test_filters(env, name, expected):
54+
when = pendulum.datetime(2012, 7, 24, 3, 4, 52, tz='UTC')
55+
result = env.from_string('{{ date |' + name + ' }}').render(date=when)
56+
assert result == expected

0 commit comments

Comments
 (0)