<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>TreasureData | Democratizing Data</title><link>https://b94feae1.chezo-uno.pages.dev/categories/treasuredata/</link><atom:link href="https://b94feae1.chezo-uno.pages.dev/categories/treasuredata/index.xml" rel="self" type="application/rss+xml"/><description>TreasureData</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><copyright>© Aki Ariga 2017-2025</copyright><lastBuildDate>Tue, 24 Dec 2019 00:00:00 +0900</lastBuildDate><image><url>https://b94feae1.chezo-uno.pages.dev/media/icon_hu_9ca82a90e0d162db.png</url><title>TreasureData</title><link>https://b94feae1.chezo-uno.pages.dev/categories/treasuredata/</link></image><item><title>Pythonistaのためのdigdag py> operator開発ガイド</title><link>https://b94feae1.chezo-uno.pages.dev/post/2019-12-24-python-custom-scripting/</link><pubDate>Tue, 24 Dec 2019 00:00:00 +0900</pubDate><guid>https://b94feae1.chezo-uno.pages.dev/post/2019-12-24-python-custom-scripting/</guid><description>
&lt;details class="print:hidden xl:hidden" >
&lt;summary>Table of Contents&lt;/summary>
&lt;div class="text-sm">
&lt;nav id="TableOfContents">
&lt;ul>
&lt;li>&lt;a href="#workflowをpushするまえにローカル環境で開発とテストをする方法">workflowをpushするまえにローカル環境で開発とテストをする方法&lt;/a>
&lt;ul>
&lt;li>&lt;a href="#基本戦略pythonのタスクを合理的な粒度にまとめる">基本戦略:Pythonのタスクを合理的な粒度にまとめる&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-treasure-dataの公式dockerイメージを使う">1. Treasure Dataの公式Dockerイメージを使う&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-dockerイメージと同様のpythonの仮想環境を作る">2. Dockerイメージと同様のPythonの仮想環境を作る&lt;/a>&lt;/li>
&lt;li>&lt;a href="#py-operatorを含んだworkflowをローカルで実行する">py&amp;gt; operatorを含んだworkflowをローカルで実行する&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-digdagの引数を使う">1. digdagの引数を使う&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-環境変数を使う">2. 環境変数を使う&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-digdagの変数を使う">3. digdagの変数を使う&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="#pythonやosのパッケージのインストール方法">PythonやOSのパッケージのインストール方法&lt;/a>&lt;/li>
&lt;li>&lt;a href="#py-operatorを含むディレクトリ構成">py&amp;gt; operatorを含むディレクトリ構成&lt;/a>&lt;/li>
&lt;li>&lt;a href="#実行時のエラーを通知する">実行時のエラーを通知する&lt;/a>&lt;/li>
&lt;li>&lt;a href="#まとめ">まとめ&lt;/a>&lt;/li>
&lt;/ul>
&lt;/nav>
&lt;/div>
&lt;/details>
&lt;p>この記事は、&lt;a href="https://adventar.org/calendars/3932" target="_blank" rel="noopener">Arm Treasure Data Advent Calendar 2019&lt;/a>の24日目です。&lt;/p>
&lt;p>今年の夏に新しくTreasure Dataで使えるようになったPython Custom Scriptingですが、
開発する際にどういう点を気をつければ良いのかという質問をいただくことが多いので、
今日はTreasure Workflowとdigdagのpy&amp;gt; operatorを使った開発の際に気をつけることを書いて行こうと思います。&lt;/p>
&lt;p>なお、Treasure WorkflowとありますがOSSのdigdagでも活かせる内容があると思います。&lt;/p>
&lt;p>なお、既にPython Custom Scriptingが試せる環境にある方は、このGoogle Colab notebook &lt;a href="https://bit.ly/191212_cs" target="_blank" rel="noopener">https://bit.ly/191212_cs&lt;/a> で一通り試すことが出来ます。&lt;/p>
&lt;h2 id="workflowをpushするまえにローカル環境で開発とテストをする方法">workflowをpushするまえにローカル環境で開発とテストをする方法&lt;/h2>
&lt;h3 id="基本戦略pythonのタスクを合理的な粒度にまとめる">基本戦略:Pythonのタスクを合理的な粒度にまとめる&lt;/h3>
&lt;p>Treasure Workflowやdigdagのサーバーモードでは、タスク間の中間ファイルを置くためのストレージがないため、一つ一つのタスクの粒度が大きくなりがちです。
特に、Treasure Workflowでは一つ一つのtaskでコンテナが起動するため、そのオーバーヘッドを無視できません。
一方で、一つの巨大なタスクは、開発時のデバッグを難しくさせます。
そこで、開発時の戦略としては一つ一つは最低限の塊に分けた関数を用意します。そして、必要に応じてそれらを束ねる関数を用意するのが良いでしょう。&lt;/p>
&lt;p>Treasure Workflow向けのworkflowをローカル環境で開発するにはいくつかの選択肢があります。&lt;/p>
&lt;ol>
&lt;li>Treasure Dataの公式Dockerイメージを使う&lt;/li>
&lt;li>Dockerイメージと同じPythonの仮想環境を使う&lt;/li>
&lt;/ol>
&lt;h3 id="1-treasure-dataの公式dockerイメージを使う">1. Treasure Dataの公式Dockerイメージを使う&lt;/h3>
&lt;p>基本的にはTreasure Dataが提供している公式イメージを利用すると、本番環境と同じイメージが使うことができるので良いでしょう。&lt;/p>
&lt;p>その際には、他のPythonスクリプトの開発と同様に、切り分けた単体で実行できるようにして開発するのが良いでしょう。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">your_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;default_argument&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>2019/12/23現在、最新のDocker imageは以下の2つです。&lt;/p>
&lt;ul>
&lt;li>digdag/digdag-python:3.7 &lt;a href="https://hub.docker.com/r/digdag/digdag-python" target="_blank" rel="noopener">https://hub.docker.com/r/digdag/digdag-python&lt;/a>&lt;/li>
&lt;li>digdag/digdag-anaconda3:2019.03 &lt;a href="https://hub.docker.com/r/digdag/digdag-anaconda3" target="_blank" rel="noopener">https://hub.docker.com/r/digdag/digdag-anaconda3&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>PyCharmのProfessional Editionを持っている場合、Dockerのコンテナにremote debuggerがアタッチできるので便利です。
詳しくは&lt;a href="https://www.jetbrains.com/help/pycharm/using-docker-as-a-remote-interpreter.html" target="_blank" rel="noopener">PyCharmのドキュメント&lt;/a>をご参照ください。&lt;/p>
&lt;h3 id="2-dockerイメージと同様のpythonの仮想環境を作る">2. Dockerイメージと同様のPythonの仮想環境を作る&lt;/h3>
&lt;p>Python は仮想環境を作るための &lt;code>venv&lt;/code> と呼ばれるモジュールがあります。
これを使うことで他のプロジェクトと分離したパッケージの環境を作ることができます。&lt;/p>
&lt;h4 id="digdag-python37">digdag-python:3.7&lt;/h4>
&lt;p>digdag-python:3.7と同様の環境を作るには、次のgistからrequirements.txtとconstraints.txtをダウンロードします。
&lt;a href="https://gist.github.com/chezou/d0a0fc62007af4d808752e78b31ae694" target="_blank" rel="noopener">https://gist.github.com/chezou/d0a0fc62007af4d808752e78b31ae694&lt;/a>&lt;/p>
&lt;p>その後、以下のようにコマンドを実行することでDockerイメージと同様のPython環境が構築できます。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ python -m venv .venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ &lt;span class="nb">source&lt;/span> .venv/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">(&lt;/span>.venv&lt;span class="o">)&lt;/span>$ pip install -r requirements.txt -c constraints.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="flex px-4 py-3 mb-6 rounded-md bg-primary-100 dark:bg-primary-900">
&lt;span class="pr-3 pt-1 text-primary-600 dark:text-primary-300">
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m11.25 11.25l.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0m-9-3.75h.008v.008H12z"/>&lt;/svg>
&lt;/span>
&lt;span class="dark:text-neutral-300">この方法では、OSによる差異まで吸収ができない場合があります。
例えば、digdag-python:3.7はDebianベースなので、OSレベルのパッケージ管理は &lt;code>apt-get&lt;/code> を使いますが、それをスクリプト内に書いても実行ができません。&lt;/span>
&lt;/div>
&lt;h4 id="digdag-anaconda3201903">digdag-anaconda3:2019.03&lt;/h4>
&lt;p>anacondaイメージと同じ環境を作りたい場合は、gistからenvironment.ymlをダウンロードします。
&lt;a href="https://gist.github.com/chezou/d0a0fc62007af4d808752e78b31ae694#file-environment-yml" target="_blank" rel="noopener">https://gist.github.com/chezou/d0a0fc62007af4d808752e78b31ae694#file-environment-yml&lt;/a>&lt;/p>
&lt;p>その後、以下のコマンドを実行するとデフォルトの環境に依存するパッケージをインストールします。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">conda env update -n base -f environment.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>なお、新しい環境を構築したい場合は、environment.ymlの中の &lt;code>base&lt;/code> を &lt;code>my-env&lt;/code> など好きな名前に変更した後、以下のコマンドで新規仮想環境を作成してください。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">conda env create -f environment.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="py-operatorを含んだworkflowをローカルで実行する">py&amp;gt; operatorを含んだworkflowをローカルで実行する&lt;/h3>
&lt;p>もし、ローカル環境でworkflow全体を実行したい場合は、 &lt;del>&lt;a href="https://github.com/treasure-data/digdag/tree/v0_10" target="_blank" rel="noopener">digdagの v0_10ブランチ&lt;/a>を使うことで、
本番環境に近いdigdag環境が用意できます&lt;/del>。&lt;/p>
&lt;div class="flex px-4 py-3 mb-6 rounded-md bg-yellow-100 dark:bg-yellow-900">
&lt;span class="pr-3 pt-1 text-red-400">
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/>&lt;/svg>
&lt;/span>
&lt;span class="dark:text-neutral-300">2019/12/23現在は、Treasure Dataではv0_10を使っていますが、将来これは変わる恐れがあります。&lt;/span>
&lt;/div>
&lt;div class="flex px-4 py-3 mb-6 rounded-md bg-yellow-100 dark:bg-yellow-900">
&lt;span class="pr-3 pt-1 text-red-400">
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/>&lt;/svg>
&lt;/span>
&lt;span class="dark:text-neutral-300">2021/02/14現在、v0_11へと移行しました。今後は適宜適切なブランチを使うようにしてください。
&lt;a href="https://github.com/treasure-data/digdag/pull/1502" target="_blank" rel="noopener">https://github.com/treasure-data/digdag/pull/1502&lt;/a>
&lt;a href="https://github.com/treasure-data/digdag/pull/1504" target="_blank" rel="noopener">https://github.com/treasure-data/digdag/pull/1504&lt;/a>&lt;/span>
&lt;/div>
## py> operatorにパラメータを渡す
&lt;p>py&amp;gt; operatorにパラメータを渡すには2つの方法があります。&lt;/p>
&lt;ol>
&lt;li>digdagの引数を使う&lt;/li>
&lt;li>環境変数を使う&lt;/li>
&lt;li>digdagの変数でやりとりする&lt;/li>
&lt;/ol>
&lt;h3 id="1-digdagの引数を使う">1. digdagの引数を使う&lt;/h3>
&lt;p>以下のような &lt;code>py_scripts/examples.py&lt;/code> というスクリプトがあるとします。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">print_arg&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Message is &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>msg&lt;/code> という変数を &lt;code>print_arg&lt;/code> という関数に渡す場合、以下のようなdigdagのtaskになります。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">+simple_with_arg&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.print_arg&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">msg&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Hello World&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>もし、複数の引数をPythonの関数へ渡したい場合、digdagの引数を増やせばよいでしょう。&lt;/p>
&lt;p>ここで注意してほしいのが、digdagの変数はPythonにシームレスに渡されるということです。
つまり、 &lt;code>**kwargs&lt;/code> などで引数を受け取る場合、意図しないdigdagの変数がPythonに渡る可能性に注意してください。&lt;/p>
&lt;p>例えば、上の例では &lt;code>docker&lt;/code> という変数には &lt;code>{&amp;quot;image&amp;quot;: &amp;quot;digdag/digdag-python:3.7&amp;quot;}&lt;/code> という辞書型の変数が代入されています。
ですので、 &lt;code>**kwargs&lt;/code> で受けるのではなく、明示的な引数の指定を強くおすすめします。&lt;/p>
&lt;p>た、同様に意図しないdigdagの変数と py&amp;gt; operatorの引数の衝突が起こる場合があります。
以下のようなワークフローがあったとします。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">_export&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">td&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">database&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my_db&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">+simple_with_arg2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.print_arg_td&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">msg&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Hello World&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>このとき、Pythonのスクリプトは以下のような関数を持っているとします。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">print_arg_td&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">td&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#39;msg&amp;#39; is &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> and &amp;#39;td&amp;#39; is &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">td&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>simple_with_arg2&lt;/code> タスク実行時に &lt;code>print_arg_td&lt;/code> 関数へ渡される引数 &lt;code>td&lt;/code> は何になるでしょうか？
通常のPythonの感覚では、デフォルト値である &lt;code>None&lt;/code> が期待されます。
しかし、 digdagのワークフローで &lt;code>td&lt;/code> という変数がすでにexportされているため、 引数 &lt;code>td&lt;/code> には &lt;code>{&amp;quot;database&amp;quot;: &amp;quot;my_db&amp;quot;}&lt;/code> という辞書が格納されてしまいます。
これにより、変数 &lt;code>td&lt;/code> の型(&lt;code>dict&lt;/code>)が期待されていたもの(&lt;code>str&lt;/code>)との不一致が起こりうるのです。&lt;/p>
&lt;p>このようなミスマッチを避けるために、digdagで予約されている変数やよく利用される変数を避けたほうが良いでしょう。
特に &lt;code>td&lt;/code> という変数や以下の変数は避けたほうが良いです。&lt;/p>
&lt;ul>
&lt;li>td.endpoint&lt;/li>
&lt;li>td.apikey&lt;/li>
&lt;li>td.use_ssl&lt;/li>
&lt;li>td.proxy.enabled&lt;/li>
&lt;li>td.proxy.host&lt;/li>
&lt;li>td.proxy.port&lt;/li>
&lt;li>td.proxy.password&lt;/li>
&lt;li>td.proxy.user&lt;/li>
&lt;/ul>
&lt;p>これらの既に利用されている変数は将来的に変更される可能性があります。&lt;/p>
&lt;p>digdagの組み込み変数は次のドキュメントをご参照ください。
&lt;a href="http://docs.digdag.io/workflow_definition.html#using-variables" target="_blank" rel="noopener">http://docs.digdag.io/workflow_definition.html#using-variables&lt;/a>&lt;/p>
&lt;p>また、digdagは &lt;code>int&lt;/code> から &lt;code>str&lt;/code> など意図しない型の変換を行う場合があります。そのため、型のチェックや明示的な変換をおすすめします。&lt;/p>
&lt;h3 id="2-環境変数を使う">2. 環境変数を使う&lt;/h3>
&lt;p>環境変数は、py&amp;gt; operatorに変数を渡すためのもう一つの方法です。&lt;/p>
&lt;p>TDのAPI keyやAWSのsecret keyなどセキュアな情報はこの形式でやりとりすることが多いです。&lt;/p>
&lt;p>例えば、以下のような &lt;code>simple_with_env&lt;/code> というタスクがあったとします。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">+simple_with_env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.print_env&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">_env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">MY_ENV_VAR&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>このとき、 &lt;code>MY_ENV_VAR&lt;/code> は以下のようにPythonから取得できます。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">print_env&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s1">&amp;#39;Env var is &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;MY_ENV_VAR&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Treasure Workflowではセキュアな情報を扱うためにdigdagの &lt;code>secrets&lt;/code> を格納するデータベースを提供しています。&lt;/p>
&lt;figure>&lt;img src="https://b94feae1.chezo-uno.pages.dev/post/2019-12-24-python-custom-scripting/digdag_secrets.png">&lt;figcaption>
&lt;h4>Digdag Secretsの設定フロー&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;ol>
&lt;li>Projectを &lt;code>td workflow push&lt;/code> でプッシュする&lt;/li>
&lt;li>secretsを &lt;code>td workflow secrets&lt;/code> でデータベースに格納する&lt;/li>
&lt;li>secretsを &lt;code>{secret:my_secret}&lt;/code> という形式でworkflowに書き、環境変数経由でPythonにわたす&lt;/li>
&lt;/ol>
&lt;p>環境変数での受け渡しをしないとsecretsはPythonに渡されません。
例えば、以下のようなワークフローとPythonスクリプトでは正しくsecretsに格納された &lt;code>td.apikey&lt;/code> という情報が渡されます。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">+simple_with_env2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.access_td&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">_env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">TD_API_KEY&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${secret:td.apikey}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">access_td&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Able to fetch API key like &amp;#34;1234/XXXX&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apikey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;TD_API_KEY&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Do awesome execution&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>しかし、以下のようにdigdagの引数として渡した場合はsecretsが評価されません。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">+simple_with_env_ng&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.access_td_ng&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">apikey&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${secret:td.apikey}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">access_td_ng&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">apikey&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Always shows &amp;#34;${secret:td.apikey}&amp;#34; instead of actual API key like &amp;#34;1234/XXXX&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">apikey&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>secretsに関する詳細は、&lt;a href="https://docs.digdag.io/command_reference.html#secrets" target="_blank" rel="noopener">digdagのドキュメント&lt;/a>をご参照ください。&lt;/p>
&lt;h3 id="3-digdagの変数を使う">3. digdagの変数を使う&lt;/h3>
&lt;p>digdagのpy&amp;gt; operatorとして実行するPythonスクリプトでは、 &lt;code>import digdag&lt;/code> とすることでdigdagモジュールを利用することが出来ます。&lt;/p>
&lt;p>以下のように、 &lt;code>digdag.env.params&lt;/code> と呼ばれる変数に辞書のようにアクセスすることでdigdagの変数を直接読むことが出来ます。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">read_workflow_env&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">digdag&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">digdag&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;my_msg&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>このとき、実行するworkflowは例えば以下のようなものになります。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">_export&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">my_msg&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;awesome message&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">+simple_with_arg2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.read_workflow_env&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>なお、読み込みだけではなく&lt;code>digdag.env.store&lt;/code>を使うことでdigdagの変数の書き込みもできます。
ただし、あまり大きな変数の受け渡しはしないほうが良いでしょう。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">store_workflow_env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">digdag&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">digdag&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;my_msg&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">msg&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>このdigdagモジュールは、digdagがpy&amp;gt; operator実行時に動的に生成しているため、ローカルでは実行できないことに注意してください。
ローカルの実行を考慮する場合は、 &lt;code>try-expect&lt;/code> を利用するのが良いでしょう。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">digdag&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">digdag&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">store&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;feature_query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">feature_query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="ne">ImportError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="pythonやosのパッケージのインストール方法">PythonやOSのパッケージのインストール方法&lt;/h2>
&lt;p>Treasure WorkflowではDocker imageに入っていないパッケージは &lt;code>os.system&lt;/code> や &lt;code>subprocess.run&lt;/code>を使い実行します。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">sys&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">executable&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> -m pip install --upgrade pytd==1.4.3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">subprocess&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># arguments should be passed by list&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">executable&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;-m&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pip&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;install&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--upgrade&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pytd==1.4.3&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>この際、パッケージのバージョンを指定しましょう。&lt;/p>
&lt;p>OSのパッケージのインストールの場合も同様です。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;apt-get update&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># Need to run before doing apt-get install&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">system&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;apt-get install -y wkhtmltopdf&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="py-operatorを含むディレクトリ構成">py&amp;gt; operatorを含むディレクトリ構成&lt;/h2>
&lt;p>一つのプロジェクトでは、以下のようなディレクトリ構成をおすすめしています。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-txt" data-lang="txt">&lt;span class="line">&lt;span class="cl">my_project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── params.test.yml &amp;lt;- Configuration file for run through test. Mirror params.yml except for `td.database`
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── params.yml &amp;lt;- Configuration file for production
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── awesome_workflow.dig &amp;lt;- Main workflow to be executed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── ingest.dig &amp;lt;- Data ingestion workflow
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── py_scripts &amp;lt;- Python scripts directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── __init__.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── data.py &amp;lt;- Script to upload data to Arm Treasure Data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── my_script.py &amp;lt;- Main script to execute e.g. Data enrichment, ML training
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── queries &amp;lt;- SQL directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── example.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── run_test.sh &amp;lt;- Test shell script for local run through test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── test.dig &amp;lt;- Test workflow for local run through test
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>この構成に従ったdigdagプロジェクトをcookiecutter-digdagを使うことで簡単にテンプレートから生成できます。
&lt;a href="https://github.com/chezou/cookiecutter-digdag" target="_blank" rel="noopener">https://github.com/chezou/cookiecutter-digdag&lt;/a>&lt;/p>
&lt;h2 id="実行時のエラーを通知する">実行時のエラーを通知する&lt;/h2>
&lt;p>Pythonのスクリプト実行時に発生したエラーをSlackなどで通知したい事があると思います。
digdagは &lt;code>_error:&lt;/code> でworkflowが失敗した際の処理をできますが、そのとき&lt;code>${error.message}&lt;/code>の中にPythonの例外情報が入っています。&lt;/p>
&lt;p>以下のようなworkflowとPython scriptがあったとします。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">+simple_raise_error&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">py&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">py_scripts.examples.error_sample&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;digdag/digdag-python:3.7&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">_error&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">echo&amp;gt;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${error.message}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-py" data-lang="py">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">error_sample&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a1234&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># raises ValueError&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>このとき、以下のようなログが得られます。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-txt" data-lang="txt">&lt;span class="line">&lt;span class="cl">2019-12-24 23:06:32 +0900 [INFO] (0039@[0:python]+simple^error): echo&amp;gt;: Python command failed with code 1: invalid literal for int() with base 10: &amp;#39;a1234&amp;#39; (ValueError)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> from Traceback (most recent call last):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> from File &amp;#34;.digdag/tmp/digdag-py-2-1815457087076518360/runner.py&amp;#34;, line 165, in &amp;lt;module&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> result = callable_type(**args)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> from File &amp;#34;/private/var/folders/y9/bnjb3krn39s22rmg_wvlnf7m0000gp/T/digdag-tempdir2111531196420040503/workspace/1_simple_1_2_2945225080250994454/py_scripts/examples.py&amp;#34;, line 5, in print_arg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> int(&amp;#34;a1234&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> from ValueError: invalid literal for int() with base 10: &amp;#39;a1234&amp;#39; (runtime)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>この例では、&lt;code>echo&amp;gt;&lt;/code> operatorでエラーを出力しているだけですが、Slack等に例外を送ることで定期実行しているPythonタスクの通知が簡単に行なえます。&lt;/p>
&lt;h2 id="まとめ">まとめ&lt;/h2>
&lt;p>このように、様々なポイントをおさえることでTreasure Workflowの開発をしやすくなるかと思います。
また、digdagでもいくらかのポイントは使うことができるかと思います。&lt;/p></description></item><item><title>RとTreasure Data</title><link>https://b94feae1.chezo-uno.pages.dev/post/2019-12-04-r-and-td/</link><pubDate>Wed, 04 Dec 2019 00:00:00 +0900</pubDate><guid>https://b94feae1.chezo-uno.pages.dev/post/2019-12-04-r-and-td/</guid><description>&lt;p>この記事は、&lt;a href="https://adventar.org/calendars/3932" target="_blank" rel="noopener">Arm Treasure Data advent calendar&lt;/a> 3日目です。&lt;/p>
&lt;p>公式ドキュメントは、 &lt;a href="https://support.treasuredata.com/hc/en-us/articles/360001487927-R-Language-Driver-Install" target="_blank" rel="noopener">Rについてはあまり書かれていない&lt;/a>のですが、RからTreasureDataを使うにはRJDBC以外にもいくつかの方法があります。&lt;/p>
&lt;p>基本的には、RPrestoとRTDをおさえておくのが良いでしょう。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/prestodb/RPresto" target="_blank" rel="noopener">RPresto&lt;/a>
&lt;ul>
&lt;li>Facebookが開発しているPrestoのR client。TreasureDataでもアドホックなクエリを投げるときに便利&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://github.com/treasure-data/RTD" target="_blank" rel="noopener">RTD&lt;/a>
&lt;ul>
&lt;li>Rからdata frameをTreasure Dataにアップロードするのに使えるR client&lt;/li>
&lt;li>(12/18追記) レポジトリの場所がtreasure-dataの下になりました&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://github.com/chezou/sparklytd" target="_blank" rel="noopener">sparklytd&lt;/a>
&lt;ul>
&lt;li>RStudio社が開発するRのSpark clientであるsparklyrのplugin&lt;/li>
&lt;li>内部で使うtd-sparkはSparkのサポートバージョンが上がるのが速いが、sparklyrのSparkの新バージョンサポートが遅いので少し低調なメンテナンス&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>実際の、RPrestoとRTDの使い方は以下のRmarkdownを見ていただくのが良いかと思います。
&lt;a href="https://rpubs.com/chezou/TD-from-RPresto-RTD" target="_blank" rel="noopener">https://rpubs.com/chezou/TD-from-RPresto-RTD&lt;/a>&lt;/p>
&lt;h1 id="rtdのインストール">RTDのインストール&lt;/h1>
&lt;p>インストールは、 &lt;code>devtools::install_github&lt;/code> を使うか、 &lt;code>install-github.me&lt;/code> を使うことでできます。&lt;/p>
&lt;p>RTDは v0.2.0 からCRANに上げるのを辞めました。これはこれで一つの記事が書けるのですがそれはまた別の機会に譲ろうと思います。&lt;/p>
&lt;p>余談ですが、CRANとの戦いの際にはr-wakalangの皆様に支えていただきました。Rに関する開発はもはやあそこなしには成り立ちません。&lt;a href="https://github.com/tokyor/r-wakalang" target="_blank" rel="noopener">Slack&lt;/a>にぜひ参加するといいと思います。パッケージングから統計モデリングも話まで物凄い勢いで有識者が答えてくれます。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-R" data-lang="R">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">source&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;https://install-github.me/treasure-data/RTD@v0.2.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>インストール後は、&lt;code>apikey&lt;/code> と &lt;code>endpoint&lt;/code> を適切なものを渡して上げることで、uploadができます。例えば &lt;code>nycflights13&lt;/code> パッケージの &lt;code>flights&lt;/code> データをアップロードしてみましょう。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-R" data-lang="R">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RTD&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">Td&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">apikey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">Sys.getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;TD_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">endpoint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;api.treasuredata.com&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">td_upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;flights&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">nycflights13&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">flights&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">overwrite&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">T&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>v0.1.0 ではembulkが必須でしたが、今のバージョンは bulk import APIを使うようになっているため、特に追加でツールをインストールする必要はありません。&lt;/p>
&lt;h1 id="rprestoでの接続">RPrestoでの接続&lt;/h1>
&lt;p>&lt;code>src_presto&lt;/code> する際には、 &lt;code>user&lt;/code> に TDのAPI keyを渡すこと、hostは&lt;a href="https://support.treasuredata.com/hc/en-us/articles/360001474288-Sites-and-Endpoints#Endpoints" target="_blank" rel="noopener">こちらのendpoint&lt;/a>を参照してください。schemaはDatabase名、 catalogは &lt;code>td-presto&lt;/code>のままにしてください。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-R" data-lang="R">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RPresto&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dplyr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">db&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">src_presto&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">host&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://api-presto.treasuredata.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">port&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">443&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nf">Sys.getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;TD_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">schema&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">catalog&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;td-presto&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">flights_tbl&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nf">tbl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#39;flights&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>flights_tbl&lt;/code> にテーブルが変数として割り当てられました。が、実際にはdplyrの諸々の処理を実行するときにSQLが走るのでalias程度に考えてください。&lt;/p>
&lt;h1 id="dplyrとggplot2で可視化する">dplyrとggplot2で可視化する&lt;/h1>
&lt;p>それでは、 &lt;code>flights_tbl&lt;/code> をdplyrを使い集計して飛行機の平均遅延時間などを算出して、ggplot2で可視化してみましょう。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-R" data-lang="R">&lt;span class="line">&lt;span class="cl">&lt;span class="n">delay&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="n">flights_tbl&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">group_by&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tailnum&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">summarise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">n&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">distance&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">na.rm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">delay&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arr_delay&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">na.rm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">filter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">count&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="m">20&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dist&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="m">2000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&amp;gt;%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">collect&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># plot delays&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ggplot2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">ggplot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">delay&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">aes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">delay&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">geom_point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">aes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">count&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">geom_smooth&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">scale_size_area&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>このような図ができたかと思います。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="flex justify-center ">
&lt;div class="w-100" >&lt;img alt="" srcset="
/post/2019-12-04-r-and-td/rpresto_ggplot_hu_bf2a05aaa53d9f47.webp 400w,
/post/2019-12-04-r-and-td/rpresto_ggplot_hu_6cbc3cd7306f8ee1.webp 760w,
/post/2019-12-04-r-and-td/rpresto_ggplot_hu_749072726ec39129.webp 1200w"
src="https://b94feae1.chezo-uno.pages.dev/post/2019-12-04-r-and-td/rpresto_ggplot_hu_bf2a05aaa53d9f47.webp"
width="760"
height="543"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>それでは、HappyなRとTreasureDataライフを！&lt;/p></description></item><item><title>tdworkflow</title><link>https://b94feae1.chezo-uno.pages.dev/project/tdworkflow/</link><pubDate>Sun, 01 Dec 2019 18:48:33 +0900</pubDate><guid>https://b94feae1.chezo-uno.pages.dev/project/tdworkflow/</guid><description/></item></channel></rss>