it-swarm.com.de

Luftstrom mit Vorlagendateien für PythonOperator

Die Methode, eine BashOperator oder SqlOperator zu erhalten, um eine externe Datei für ihre Vorlage abzurufen, ist einigermaßen dokumentiert, aber der PythonOperator mein Test dessen, was ich aus den Dokumenten verstehe, funktioniert nicht. Ich bin nicht sicher, wie die Parameter templates_exts und templates_dict korrekt zusammenwirken würden, um eine Datei abzurufen.

In meinem Dags-Ordner habe ich Folgendes erstellt: pyoptemplate.sql und pyoptemplate.t sowie test_python_operator_template.py:

pyoptemplate.sql:

SELECT * FROM {{params.table}};

pyoptemplate.t:

SELECT * FROM {{params.table}};

test_python_operator_template.py:

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

Das Ergebnis eines Laufs zeigt, dass table korrekt als Zeichenfolge erstellt wurde, die anderen Dateien jedoch keine Dateien für die Vorlage einreissen.

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

Ab Airflow 1.8 funktioniert die Art und Weise, wie der PythonOperator das Feld template_ext in __init__ ersetzt, nicht. Aufgaben überprüfen nur template_ext im __class__. Um einen PythonOperator zu erstellen, der SQL-Vorlagendateien aufnimmt, müssen Sie nur Folgendes tun:

class SQLTemplatedPythonOperator(PythonOperator):
    template_ext = ('.sql',)

Und dann auf die SQL von Ihrer Task aus zugreifen, wenn sie ausgeführt wird:

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']
10
Ardan

Ich glaube nicht, dass das wirklich möglich ist. Die folgende Problemumgehung kann jedoch hilfreich sein:

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)

Würde aber eine bessere Lösung lieben!

8
Will Fitzgerald

Vor kurzem bin ich auf das gleiche Problem gestoßen und habe es endlich gelöst. Die Lösung von @Ardan ist korrekt, möchte aber nur mit einer ausführlicheren Antwort mit einigen Details zur Funktionsweise von Airflow für die Neuankömmlinge wiederholt werden. 

Natürlich brauchst du zuerst eines davon:

from airflow.operators.python_operator import PythonOperator

class SQLTemplatedPythonOperator(PythonOperator):

    # somehow ('.sql',) doesn't work but Tuple of two works...
    template_ext = ('.sql','.abcdefg')

Angenommen, Sie haben eine SQL-Vorlagendatei wie folgt:

# stored at path: $AIRFLOW_HOME/sql/some.sql
select {{some_params}} from my_table;

Stellen Sie zunächst sicher, dass Sie Ihren Ordner zum Suchpfad in Ihren Tagparametern hinzufügen.

Template_searchpath nicht an args übergeben und dann args an DAG übergeben !!!! Es funktioniert nicht

dag = DAG(
    dag_id= "some_name",
    default_args=args,
    schedule_interval="@once",
    template_searchpath='/Users/your_name/some_path/airflow_home/sql'
)

Dann wird Ihr Operator anrufen

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

Ihre Funktion wird sein:

def your_func(args_directly_passed_to_your_function=None):
    query = context['templates_dict']['query']
    dome_some_thing(query)

Einige Erklärungen:

  1. Airflow verwendet Werte aus dem Kontext, um Ihre Vorlage zu rendern. Um es manuell zum Kontext hinzuzufügen, können Sie das Feld params wie oben verwenden.

  2. PythonOperator nimmt die Vorlage-Dateierweiterung aus dem Feld template_ext nicht mehr wie @Ardan an. Der Quellcode lautet here ..__ und hat nur die Erweiterung von self .__ class __. Template_ext.

  3. Airflow durchläuft das Feld template_dict und wenn value.endswith (file_extension) == True ist, wird die Vorlage gerendert.

6
P. Xie

Eine Skriptdatei kann nicht in Python erstellt werden (neu für Python). Aber ein Beispiel mit dem bash-Operator folgt, vielleicht kann das ein paar Hinweise geben

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)

das 01.sh-Skript sieht wie folgt aus

#!/bin/sh

echo {{ ds }}
echo {{ params.ds1 }}

Dies gibt eine Ausgabe wie folgt bei der Testausführung

[2017-05-12 08: 31: 52,981] {bash_operator.py:91} INFO - Ausgabe:

[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

1
Saurabh Mishra