La méthode permettant à un BashOperator
ou SqlOperator
de récupérer un fichier externe pour son modèle est assez bien documentée, mais l'examen de la PythonOperator
mon test de ce que je comprends des documents ne fonctionne pas. Je ne suis pas sûr de savoir comment les paramètres templates_exts
et templates_dict
interagiraient correctement pour prendre un fichier.
Dans mon dossier dags, j'ai créé: pyoptemplate.sql
et pyoptemplate.t
ainsi que test_python_operator_template.py
:
SELECT * FROM {{params.table}};
SELECT * FROM {{params.table}};
# coding: utf-8
# vim:ai:si:et:sw=4 ts=4 tw=80
"""
# A Test of Templates in PythonOperator
"""
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
from datetime import datetime
import pprint
pp = pprint.PrettyPrinter(indent=4)
def templated_function(ds, **kwargs):
"""This function will try to use templates loaded from external files"""
pp.pprint(ds)
pp.pprint(kwargs)
# Define the DAG
dag = DAG(dag_id='test_python_operator_template_dag',
default_args={"owner": "lamblin",
"start_date": datetime.now()},
template_searchpath=['/Users/daniellamblin/airflow/dags'],
schedule_interval='@once')
# Define the single task in this controller example DAG
op = PythonOperator(task_id='test_python_operator_template',
provide_context=True,
python_callable=templated_function,
templates_dict={
'pyoptemplate': '',
'pyoptemplate.sql': '',
'sql': 'pyoptemplate',
'file1':'pyoptemplate.sql',
'file2':'pyoptemplate.t',
'table': '{{params.table}}'},
templates_exts=['.sql','.t'],
params={'condition_param': True,
'message': 'Hello World',
'table': 'TEMP_TABLE'},
dag=dag)
Le résultat d'une exécution montre que table
a été correctement modélisé en tant que chaîne, mais les autres n'ont extrait aucun fichier à des fins de modélisation.
dlamblin$ airflow test test_python_operator_template_dag test_python_operator_template 2017-01-18
[2017-01-18 23:58:06,698] {__init__.py:36} INFO - Using executor SequentialExecutor
[2017-01-18 23:58:07,342] {models.py:154} INFO - Filling up the DagBag from /Users/daniellamblin/airflow/dags
[2017-01-18 23:58:07,620] {models.py:1196} INFO -
--------------------------------------------------------------------------------
Starting attempt 1 of 1
--------------------------------------------------------------------------------
[2017-01-18 23:58:07,620] {models.py:1219} INFO - Executing <Task(PythonOperator): test_python_operator_template> on 2017-01-18 00:00:00
'2017-01-18'
{ u'END_DATE': '2017-01-18',
u'conf': <module 'airflow.configuration' from '/Library/Python/2.7/site-packages/airflow/configuration.pyc'>,
u'dag': <DAG: test_python_operator_template_dag>,
u'dag_run': None,
u'ds_nodash': u'20170118',
u'end_date': '2017-01-18',
u'execution_date': datetime.datetime(2017, 1, 18, 0, 0),
u'latest_date': '2017-01-18',
u'macros': <module 'airflow.macros' from '/Library/Python/2.7/site-packages/airflow/macros/__init__.pyc'>,
u'params': { 'condition_param': True,
'message': 'Hello World',
'table': 'TEMP_TABLE'},
u'run_id': None,
u'tables': None,
u'task': <Task(PythonOperator): test_python_operator_template>,
u'task_instance': <TaskInstance: test_python_operator_template_dag.test_python_operator_template 2017-01-18 00:00:00 [running]>,
u'task_instance_key_str': u'test_python_operator_template_dag__test_python_operator_template__20170118',
'templates_dict': { 'file1': u'pyoptemplate.sql',
'file2': u'pyoptemplate.t',
'pyoptemplate': u'',
'pyoptemplate.sql': u'',
'sql': u'pyoptemplate',
'table': u'TEMP_TABLE'},
u'test_mode': True,
u'ti': <TaskInstance: test_python_operator_template_dag.test_python_operator_template 2017-01-18 00:00:00 [running]>,
u'tomorrow_ds': '2017-01-19',
u'tomorrow_ds_nodash': u'20170119',
u'ts': '2017-01-18T00:00:00',
u'ts_nodash': u'20170118T000000',
u'yesterday_ds': '2017-01-17',
u'yesterday_ds_nodash': u'20170117'}
[2017-01-18 23:58:07,634] {python_operator.py:67} INFO - Done. Returned value was: None
Depuis Airflow 1.8, la façon dont PythonOperator remplace son champ template_ext
dans __init__
ne fonctionne pas. Les tâches ne vérifient que template_ext
sur le __class__
. Pour créer un PythonOperator qui récupère les fichiers de modèle SQL, il vous suffit de procéder comme suit:
class SQLTemplatedPythonOperator(PythonOperator):
template_ext = ('.sql',)
Et ensuite, pour accéder au SQL à partir de votre tâche quand elle s'exécute:
SQLTemplatedPythonOperator(
templates_dict={'query': 'my_template.sql'},
params={'my_var': 'my_value'},
python_callable=my_func,
provide_context=True,
)
def my_func(**context):
context['templates_dict']['query']
Je ne pense pas que ce soit vraiment possible. Mais la solution de contournement suivante peut être utile:
def templated_function(ds, **kwargs):
kwargs['ds'] = ds # put ds into 'context'
task = kwargs['task'] # get handle on task
templ = open(kwargs['templates_dict']['file1']).read() # get template
sql = task.render_template('', tmpl, kwargs) # render it
pp.pprint(sql)
J'aimerais une meilleure solution, cependant!
Récemment, je suis tombé sur le même problème et l'ai finalement résolu. La solution de @Ardan est correcte mais je souhaite simplement répéter avec une réponse plus complète avec quelques détails sur le fonctionnement d’Airflow pour les nouveaux arrivants.
Bien sûr, vous avez d’abord besoin de l’une de celles-ci:
from airflow.operators.python_operator import PythonOperator
class SQLTemplatedPythonOperator(PythonOperator):
# somehow ('.sql',) doesn't work but Tuple of two works...
template_ext = ('.sql','.abcdefg')
En supposant que vous ayez un fichier de modèle SQL comme ci-dessous:
# stored at path: $AIRFLOW_HOME/sql/some.sql
select {{some_params}} from my_table;
Commencez par vous assurer que vous ajoutez votre dossier au chemin de recherche dans vos paramètres de dag.
Ne passez pas template_searchpath aux arguments, puis passez les arguments à DAG !!!! Ça ne marche pas
dag = DAG(
dag_id= "some_name",
default_args=args,
schedule_interval="@once",
template_searchpath='/Users/your_name/some_path/airflow_home/sql'
)
Ensuite, votre appel opérateur sera
SQLTemplatedPythonOperator(
templates_dict={'query': 'some.sql'},
op_kwargs={"args_directly_passed_to_your_function": "some_value"},
task_id='dummy',
params={"some_params":"some_value"},
python_callable=your_func,
provide_context=True,
dag=dag,
)
Votre fonction sera:
def your_func(args_directly_passed_to_your_function=None):
query = context['templates_dict']['query']
dome_some_thing(query)
Quelques explications:
Airflow utilise les valeurs du contexte pour rendre votre modèle. Pour l'ajouter manuellement au contexte, vous pouvez utiliser le champ params comme ci-dessus.
PythonOperator ne prend plus l'extension de fichier de modèle du champ template_ext comme @Ardan mentionné. Le code source est ici . Il ne faut que l'extension de self .__ class __. Template_ext.
Le flux d'air parcourt le champ template_dict et si value.endswith (extension_fichier) == True, il restitue le modèle.
Impossible de faire fonctionner un fichier de script basé sur un modèle python (nouveau sur python). Mais un exemple avec l'opérateur bash suit, peut-être que cela peut vous donner des indices
from datetime import datetime
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
default_args = {
'owner': 'airflow',
'depends_on_past': False,
#'start_date': airflow.utils.dates.days_ago(2),
'email': ['[email protected]']}
dag = DAG('sr5', description='Simple tutorial DAG',
schedule_interval='0 12 * * *',
start_date=datetime(2017, 3, 20),
catchup=False, #so that on scehduler restart, it doesn't try to catchup on all the missed runs
template_searchpath=['/Users/my_name/Desktop/utils/airflow/resources'])
t1 = BashOperator(
task_id='t1',
depends_on_past=False,
params={
'ds1': 'hie'},
bash_command="01.sh",
dag=dag)
le script 01.sh ressemble à ce qui suit
#!/bin/sh
echo {{ ds }}
echo {{ params.ds1 }}
Cela donne un résultat comme suit lors de l'exécution du test
[2017-05-12 08: 31: 52,981] {bash_operator.py:91} INFO - Résultat:
[2017-05-12 08: 31: 52,984] {bash_operator.py:95} INFO - 2017-05-05
[2017-05-12 08: 31: 52,984] {bash_operator.py:95} INFO - hie